首页 > 解决方案 > 无法放大堆积条形图 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>

标签: javascriptd3.js

解决方案


关于这个问题,您有两个主要问题:

  1. 您没有在缩放/拖动事件中选择任何内容:

在缩放/拖动功能中:

 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。

  1. 您已经更改了传递 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>

这个问题没有解决对剪辑路径的需求,因此我没有将其包含在我的答案中,但是关于该主题的大量问题应该会有所帮助,例如几天前的这个问题。


推荐阅读