在下面的代码中,我设法使画笔与zoom事件保持一致,但是现在我不得不尝试在使用画笔时使条形图正确缩放。
我的brushed
函数基于this答案,但无法将逻辑转换为我自己的代码。
我收到错误消息:无法读取未定义的属性'transform',我也不知道为什么。
这是我所有的代码:
const data = [
{month: "jan", value: 12},
{month: "feb", value: 25},
{month: "mar", value: 10},
{month: "apr", value: 15}
];
var svg = d3.select("svg"),
margin = {top: 10, right: 35, bottom: 10, left: 35},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// === Bar ===
var x = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([margin.left * 2, width])
var y = d3.scaleBand()
.domain(data.map(d => d.month))
.range([height, 0])
.padding(0.1)
var yAxis = g => g
.attr("transform", `translate(${margin.left * 2},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
g.append("g").selectAll(".bar")
.data(data).enter().append("rect")
.attr("fill", "steelblue")
.attr("class", "bar")
.attr("x", x(0))
.attr("y", d => y(d.month))
.attr("width", d => x(d.value) - x(0))
.attr("height", y.bandwidth());
g.append("g")
.attr("class", "y-axis")
.call(yAxis);
// === Brush ===
var xB = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, margin.left]);
var yB = d3.scaleBand()
.domain(data.map(d => d.month))
.range([height, 0])
.padding(0.1);
var brush = d3.brushY()
.extent([[0, 0],[margin.left, height]])
.on("start brush", brushed);
var yAxisB = g => g
.call(d3.axisLeft(yB).tickSizeOuter(0))
g.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, yB.range().reverse())
g.append("g").selectAll(".brushBar")
.data(data).enter().append("rect")
.attr("fill", "steelblue")
.attr("class", "brushBar")
.attr("x", xB(0))
.attr("y", d => yB(d.month))
.attr("width", d => xB(d.value) - xB(0))
.attr("height", yB.bandwidth());
g.append("g")
.call(yAxisB);
// === Brush & Zoom ===
var bExtent = [[0, 0], [width, height]]
var zoom = d3.zoom()
.scaleExtent([1, 2])
.translateExtent(bExtent)
.extent(bExtent)
.on("zoom", zoomed)
g.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
function zoomed() {
if (d3.event.sourceEvent &&
d3.event.sourceEvent.type === "brush") return;
var t = d3.event.transform;
y.range([height, 0]
.map(d => d3.event.transform.applyY(d)));
g.selectAll(".bar")
.attr("y", d => y(d.month))
.attr("height", y.bandwidth());
g.selectAll(".y-axis").call(yAxis);
g.select(".brush").call(brush.move,
yB.range().reverse().map(t.invertY, t))
}
function brushed() {
if (d3.event.sourceEvent &&
d3.event.sourceEvent.type === "zoom") return;
var s = d3.event.selection,
nD = [];
yB.domain().forEach((d) => {
var pos = yB(d) + yB.bandwidth() / 2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
y.domain(nD);
g.selectAll(".y-axis").call(yAxis);
g.selectAll(".bar")
.attr("y", d => y(d.month))
.attr("height", y.bandwidth());
//g.select(".zoom").call(zoom.transform, d3.zoomIdentity
// .scale(2)
// .translate(-s[0], 0));
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="540" height="120"></svg>
答案 0 :(得分:1)
首先,我没有收到您描述的错误。
您的代码段中的问题似乎是,拉出的条形堆积在SVG的顶部。这是由于您正在更改y
标度的域,并且由于某些条形图会为undefined
获得y(d.month)
。因此,它们只是从0
的{{1}}位置而不是从视图中消失了。
最好的选择是重新考虑所有的画笔/缩放代码。但是,为了在代码中进行最少的更改,一种快速而肮脏的解决方案就是将具有y
位置的undefined
的条变成透明:
y
此外,为笔刷中的条形设置.style("opacity", d => y(d.month) ? 1 : 0)
,以提供更好的用户体验:
pointer-events: none;
这是您所做的更改的代码:
.attr("pointer-events", "none")
const data = [{
month: "jan",
value: 12
},
{
month: "feb",
value: 25
},
{
month: "mar",
value: 10
},
{
month: "apr",
value: 15
}
];
var svg = d3.select("svg"),
margin = {
top: 10,
right: 35,
bottom: 10,
left: 35
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var g = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// === Bar ===
var x = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([margin.left * 2, width])
var y = d3.scaleBand()
.domain(data.map(d => d.month))
.range([height, 0])
.padding(0.1)
var yAxis = g => g
.attr("transform", `translate(${margin.left * 2},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
g.append("g").selectAll(".bar")
.data(data).enter().append("rect")
.attr("fill", "steelblue")
.attr("class", "bar")
.attr("x", x(0))
.attr("y", d => y(d.month))
.attr("width", d => x(d.value) - x(0))
.attr("height", y.bandwidth());
g.append("g")
.attr("class", "y-axis")
.call(yAxis);
// === Brush ===
var xB = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, margin.left]);
var yB = d3.scaleBand()
.domain(data.map(d => d.month))
.range([height, 0])
.padding(0.1);
var brush = d3.brushY()
.extent([
[0, 0],
[margin.left, height]
])
.on("start brush", brushed);
var yAxisB = g => g
.call(d3.axisLeft(yB).tickSizeOuter(0))
g.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, yB.range().reverse())
g.append("g").selectAll(".brushBar")
.data(data).enter().append("rect")
.attr("fill", "steelblue")
.attr("class", "brushBar")
.attr("pointer-events", "none")
.attr("x", xB(0))
.attr("y", d => yB(d.month))
.attr("width", d => xB(d.value) - xB(0))
.attr("height", yB.bandwidth());
g.append("g")
.call(yAxisB);
// === Brush & Zoom ===
var bExtent = [
[0, 0],
[width, height]
]
var zoom = d3.zoom()
.scaleExtent([1, 2])
.translateExtent(bExtent)
.extent(bExtent)
.on("zoom", zoomed)
g.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
function zoomed() {
if (d3.event.sourceEvent &&
d3.event.sourceEvent.type === "brush") return;
var t = d3.event.transform;
y.range([height, 0]
.map(d => d3.event.transform.applyY(d)));
g.selectAll(".bar")
.attr("y", d => y(d.month))
.attr("height", y.bandwidth());
g.selectAll(".y-axis").call(yAxis);
g.select(".brush").call(brush.move,
yB.range().reverse().map(t.invertY, t))
}
function brushed() {
if (d3.event.sourceEvent &&
d3.event.sourceEvent.type === "zoom") return;
var s = d3.event.selection,
nD = [];
yB.domain().forEach((d) => {
var pos = yB(d) + yB.bandwidth() / 2;
if (pos > s[0] && pos < s[1]) {
nD.push(d);
}
});
y.domain(nD);
g.selectAll(".y-axis").call(yAxis);
g.selectAll(".bar")
.attr("y", d => y(d.month))
.style("opacity", d => y(d.month) ? 1 : 0)
.attr("height", y.bandwidth());
//g.select(".zoom").call(zoom.transform, d3.zoomIdentity
// .scale(2)
// .translate(-s[0], 0));
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}