javascript - 无法放大堆积条形图 d3.js V4
问题描述
我尝试使用 d3.js v4 构建带有画笔和缩放的堆叠条形图。我能够渲染图表和画笔。但是,当尝试放大时,只有轴发生变化,实际图形保持不变。我在这里附上片段。
参考:https ://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
提前感谢您的帮助。
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.axis {
stroke-width: 0.5px;
stroke: #888;
font: 10px avenir next, sans-serif;
}
.axis > path {
stroke: #888;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 90, left: 50},
margin2 = {top: 230, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
height2 = 300 - margin2.top - margin2.bottom;
var parseTime = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
z = d3.scaleOrdinal().range(["#a54300","#ec983d", "#ecc43d", "#f9ec86","#cbe989"]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("https://gist.githubusercontent.com/Udayshan07/320ea33bacc3e04f9bd77538007527a0/raw/06e65434349e63ed6cf85c4d3e9cb383e888010c/sp500.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.Week_Num = parseTime(d.Week_Num);
});
var xMin = d3.min(data, function(d) { return d.Week_Num; });
var yMax = Math.max(20, d3.max(data, function(d) { return d.total; }));
var keys = data.columns.slice(1);
var data = d3.stack().keys(keys)(data);
x.domain([xMin, new Date()]);
y.domain([0, yMax]);
x2.domain(x.domain());
y2.domain(y.domain())
z.domain(keys);
var messages = focus.append("g");
messages.selectAll("message")
.data(data)
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.Week_Num); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", 15)
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
console.log(d);
var xPosition = d3.mouse(this)[0] - 5;
var yPosition = d3.mouse(this)[1] - 5;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d[1]-d[0]);
});
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis.ticks(10))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
});
function brushed() {
//console.log('Brush 1');
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// console.log('Brush 2');
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".message")
.attr("x", function(d) { return x(d.Week_Num); })
.attr("y", function(d) { colsole.log("Brush" + y(d[1])); return y(d[1]); });
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
// console.log('Zoom 1');
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return;
// ignore zoom-by-brush
// console.log('Zoom 2');
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".message")
.attr("x", function(d) { return x(d.Week_Num); })
.attr("y", function(d) { colsole.log("Zoom" + y(d[1])); return y(d[1]);});
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 60)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 30)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
</script>
解决方案
关于这个问题,您有两个主要问题:
- 您没有在缩放/拖动事件中选择任何内容:
在缩放/拖动功能中:
focus.selectAll(".message")
.attr("x", function(d) { return x(d.Week_Num); })
.attr("y", function(d) { colsole.log("Brush" + y(d[1])); return y(d[1]); });
没有项目具有该类message
,因此这是一个空选择 - 这就是该命令colsole.log
不会出错的原因,它永远不会运行。您可以只选择rect
焦点选择中的所有 s (或给每个 rect 消息类)。此外,您不需要重新定位 y 值,也不需要重新调整 y 值,因此它可以保持不变,我已在下面的代码段中将其删除
一旦改变,我们就会遇到问题2。
- 您已经更改了传递 x 比例的值。
rect
当您最初为每个您使用的 x 赋予一个属性时:x(d.data.Week_Num);
,但在您使用的拖动/缩放功能中:x(d.Week_Num);
,这很容易解决。这给了我们:
var margin = {top: 20, right: 20, bottom: 90, left: 50},
margin2 = {top: 230, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
height2 = 300 - margin2.top - margin2.bottom;
var parseTime = d3.timeParse("%Y-%m-%d");
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
z = d3.scaleOrdinal().range(["#a54300","#ec983d", "#ecc43d", "#f9ec86","#cbe989"]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("https://gist.githubusercontent.com/Udayshan07/320ea33bacc3e04f9bd77538007527a0/raw/06e65434349e63ed6cf85c4d3e9cb383e888010c/sp500.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.Week_Num = parseTime(d.Week_Num);
});
var xMin = d3.min(data, function(d) { return d.Week_Num; });
var yMax = Math.max(20, d3.max(data, function(d) { return d.total; }));
var keys = data.columns.slice(1);
var data = d3.stack().keys(keys)(data);
x.domain([xMin, new Date()]);
y.domain([0, yMax]);
x2.domain(x.domain());
y2.domain(y.domain())
z.domain(keys);
var messages = focus.append("g");
messages.selectAll("message")
.data(data)
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.Week_Num); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", 15)
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
console.log(d);
var xPosition = d3.mouse(this)[0] - 5;
var yPosition = d3.mouse(this)[1] - 5;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d[1]-d[0]);
});
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis.ticks(10))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
});
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll("rect")
.attr("x", function(d) { return x(d.data.Week_Num); })
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return;
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll("rect")
.attr("x", function(d) { return x(d.data.Week_Num); });
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 60)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 30)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.axis {
stroke-width: 0.5px;
stroke: #888;
font: 10px avenir next, sans-serif;
}
.axis > path {
stroke: #888;
}
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
这个问题没有解决对剪辑路径的需求,因此我没有将其包含在我的答案中,但是关于该主题的大量问题应该会有所帮助,例如几天前的这个问题。
推荐阅读
- javascript - 随机类型错误:无法获取
- iot - 尝试连接主机
- matlab - 可以移动 Hounsfield 单位吗?
- tensorflow - 如何在 jupyter notebook 中使用远程机器的 GPU
- arrays - 如何读取缓冲区的前 n 个字节并在 NodeJS 中转换为字符串?
- javascript - 文本输入创建选择选项
- regex - 正则表达式限制字符长度而不匹配字符串的最后一个字符
- python-3.x - 列表中的 Python 分组
- javascript - 从 html 表单获取数据时出现错误 409
- ios - SwiftUI 中的 List 是否复用类似于 UITableView 的单元格?