【问题标题】:D3.js Zoom on area graph, weird behaivor if y domain takes negative valueD3.js放大面积图,y域的奇怪行为取负值
【发布时间】:2021-02-13 02:24:38
【问题描述】:

我一直在尝试做一个带缩放的区域图,除非我给 Y 域一个负值,否则效果很好。如果我不尝试沿 y 轴缩放,它看起来很棒

我已经尝试使用 y 的最小值作为 y0 并且修复了渲染(它看起来很糟糕,它在没有值的地方渲染 X)。

// set the dimensions and margins of the graph
let margin = {
    top: 0,
    right: 0,
    bottom: 30,
    left: 30
  },
  width = 440 - margin.left - margin.right,
  height = 240 - margin.top - margin.bottom;

// append the svg object to the body of the page
let svg = d3.select("#my_dataviz")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

//Read the data
d3.csv("https://raw.githubusercontent.com/dimitriiBirsan/RandomNumbersYear1970-2031/main/testing%20negative%20numbers.csv",

  // When reading the csv, I must format variables:
  function(d) {
    return {
      date: d3.timeParse("%m/%d/%Y")(d.date),
      value: d.value
    }
  },

  // Now I can use this dataset:
  function(data) {
    // Add X axis --> it is a date format
    let x = d3.scaleTime()
      .domain(d3.extent(data, function(d) {
        return +d.date;
      }))
      .range([0, width]);
    let xAxis = svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x).ticks(4));

    // Add Y axis
    let y = d3.scaleLinear()
      .domain(d3.extent(data, function(d) {
        return +d.value;
      }))
      .range([height, 0]);
    let yAxis = svg.append("g")
      .call(d3.axisLeft(y));

    function make_x_gridlines(x) {
      return d3.axisBottom(x)
    }

    function make_y_gridlines(y) {
      return d3.axisLeft(y)
    }

    // Add a clipPath : everything out of this area won't be drawn
    let clip = svg.append("defs").append("svg:clipPath")
      .attr("id", "clip")
      .append("svg:rect")
      .attr("width", width)
      .attr("height", height)
      .attr("x", 0)
      .attr("y", 0);

    // Add the area
    let line = svg.append('g')
      .attr("clip-path", "url(#clip)")
    line.append("path")
      .datum(data)
      .attr("class", "polyArea")
      .attr("fill", "blue")
      .attr("opacity", 0.7)
      .attr("stroke", "none")
      .attr("stroke-width", 1.5)
      .attr("d", d3.area()
        .x(function(d) {
          return x(d.date)
        })
        .y0(y(0))
        .y1(function(d) {
          return y(d.value)
        })
        .curve(d3.curveStepAfter)
        .defined((d, i) => (i != null))
      );

    let zoom = d3.zoom()
      .scaleExtent([1, 50]) // This control how much you can unzoom (x0.5) and zoom (x20)
      .translateExtent([
        [0, 0],
        [width, height]
      ])
      .extent([
        [0, 0],
        [width, height]
      ])
      .on("zoom", updateChart);

    // This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
    svg.append("rect")
      .attr("width", width)
      .attr("height", height)
      .style("fill", "none")
      .style("pointer-events", "all")
      .attr('transform', 'translate(' + margin.top + ')')
      .call(zoom);

    function updateChart() {
      // recover the new scale
      let newX = d3.event.transform.rescaleX(x);
      let newY = d3.event.transform.rescaleY(y);

      // update axes with these new boundaries
      xAxis.call(d3.axisBottom(newX).ticks(5))
      yAxis.call(d3.axisLeft(newY))
      // update location
      svg
        .select('.polyArea')
        .attr("d", d3.area()
          .x(function(d) {
            return newX(d.date)
          })
          .y0(y(0))
          .y1(function(d) {
            return newY(d.value)
          })
          .curve(d3.curveStepAfter)
          .defined((d, i) => (i != null)))
    }
  })
.grid line {
  stroke: grey;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  stroke-width 0;
}
<main>
  <div id="my_dataviz">
  </div>
</main>
<script src="https://d3js.org/d3.v4.js"></script>

【问题讨论】:

    标签: javascript d3.js zooming


    【解决方案1】:

    您忘记使用newY(0) 而不是y(0),所以现在当您缩放时,零线是它应该在的位置,而不是您开始时的位置

    // set the dimensions and margins of the graph
    let margin = {
        top: 0,
        right: 0,
        bottom: 30,
        left: 30
      },
      width = 440 - margin.left - margin.right,
      height = 240 - margin.top - margin.bottom;
    
    // append the svg object to the body of the page
    let svg = d3.select("#my_dataviz")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");
    
    //Read the data
    d3.csv("https://raw.githubusercontent.com/dimitriiBirsan/RandomNumbersYear1970-2031/main/testing%20negative%20numbers.csv",
    
      // When reading the csv, I must format variables:
      function(d) {
        return {
          date: d3.timeParse("%m/%d/%Y")(d.date),
          value: d.value
        }
      },
    
      // Now I can use this dataset:
      function(data) {
        // Add X axis --> it is a date format
        let x = d3.scaleTime()
          .domain(d3.extent(data, function(d) {
            return +d.date;
          }))
          .range([0, width]);
        let xAxis = svg.append("g")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x).ticks(4));
    
        // Add Y axis
        let y = d3.scaleLinear()
          .domain(d3.extent(data, function(d) {
            return +d.value;
          }))
          .range([height, 0]);
        let yAxis = svg.append("g")
          .call(d3.axisLeft(y));
    
        function make_x_gridlines(x) {
          return d3.axisBottom(x)
        }
    
        function make_y_gridlines(y) {
          return d3.axisLeft(y)
        }
    
        // Add a clipPath : everything out of this area won't be drawn
        let clip = svg.append("defs").append("svg:clipPath")
          .attr("id", "clip")
          .append("svg:rect")
          .attr("width", width)
          .attr("height", height)
          .attr("x", 0)
          .attr("y", 0);
    
        // Add the area
        let line = svg.append('g')
          .attr("clip-path", "url(#clip)")
        line.append("path")
          .datum(data)
          .attr("class", "polyArea")
          .attr("fill", "blue")
          .attr("opacity", 0.7)
          .attr("stroke", "none")
          .attr("stroke-width", 1.5)
          .attr("d", d3.area()
            .x(function(d) {
              return x(d.date)
            })
            .y0(y(0))
            .y1(function(d) {
              return y(d.value)
            })
            .curve(d3.curveStepAfter)
            .defined((d, i) => (i != null))
          );
    
        let zoom = d3.zoom()
          .scaleExtent([1, 50]) // This control how much you can unzoom (x0.5) and zoom (x20)
          .translateExtent([
            [0, 0],
            [width, height]
          ])
          .extent([
            [0, 0],
            [width, height]
          ])
          .on("zoom", updateChart);
    
        // This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
        svg.append("rect")
          .attr("width", width)
          .attr("height", height)
          .style("fill", "none")
          .style("pointer-events", "all")
          .attr('transform', 'translate(' + margin.top + ')')
          .call(zoom);
    
        function updateChart() {
          // recover the new scale
          let newX = d3.event.transform.rescaleX(x);
          let newY = d3.event.transform.rescaleY(y);
    
          // update axes with these new boundaries
          xAxis.call(d3.axisBottom(newX).ticks(5))
          yAxis.call(d3.axisLeft(newY))
          // update location
          svg
            .select('.polyArea')
            .attr("d", d3.area()
              .x(function(d) {
                return newX(d.date)
              })
              .y0(newY(0))
              .y1(function(d) {
                return newY(d.value)
              })
              .curve(d3.curveStepAfter)
              .defined((d, i) => (i != null)))
        }
      })
    .grid line {
      stroke: grey;
      stroke-opacity: 0.7;
      shape-rendering: crispEdges;
    }
    
    .grid path {
      stroke-width 0;
    }
    <main>
      <div id="my_dataviz">
      </div>
    </main>
    <script src="https://d3js.org/d3.v4.js"></script>

    【讨论】:

      猜你喜欢
      • 2018-03-21
      • 1970-01-01
      • 2015-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-17
      • 2018-12-18
      • 2013-12-04
      相关资源
      最近更新 更多