D3 V4 鼠标缩放和程序化缩放相互独立

D3 V4 zoom via mouse and programmatic are zooming independent from each other

我正在尝试使用 d3 建立一个应该可以缩放的趋势。那工作得很好。它可以在趋势内部缩放(缩放所有轴)并通过鼠标滚轮按每个轴缩放。

现在我想通过单击按钮添加一些缩放。这些缩放也工作正常。

通过按钮在X方向上放大,然后通过鼠标滚轮在Y方向或趋势上进行放大是完美的。

但是在 X 方向上通过按钮缩放和 afterwoods 通过鼠标滚轮在 X 方向上缩放之后,趋势开始从默认值开始缩放。

这是我的例子:https://jsfiddle.net/DaWa/d5fdLwv0/

<label>X</label>
<button onclick="zoomXIn()">+</button>
<button onclick="zoomXOut()">-</button>
<br/>
<label>Y</label>
<button onclick="zoomYIn()">+</button>
<button onclick="zoomYOut()">-</button>
<br/>
<svg width="960" height="500"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var xyTransform;
  var xTransform;
  var yTransform;

  function zoomXIn() {
    xZoom.scaleBy(xGroup, 2);
  }

  function zoomXOut() {
    xZoom.scaleBy(xGroup, 0.5);
  }

  function zoomYIn() {
    yZoom.scaleBy(yGroup, 2);
  }

  function zoomYOut() {
    yZoom.scaleBy(yGroup, 0.5);
  }

  var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 110, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;

  var xScale = d3.scaleTime().range([0, width]),
    yScale = d3.scaleLinear().range([height, 0]),
    yOld, xOld;

  var xAxis = d3.axisBottom(xScale),
    yAxis = d3.axisLeft(yScale);

  var xZoom = d3.zoom()
    .on("zoom", function () {
      xTransform = d3.event.transform;
      zoomBoth("x")
    });
  var yZoom = d3.zoom()
    .on("zoom", function () {
      yTransform = d3.event.transform;
      zoomBoth("y")
    });
  var zoom = d3.zoom()
    .on("zoom", function () {
      xyTransform = d3.event.transform;
      setDefault();
      zoomBoth()
    });

  function setDefault() {
    if (!xTransform) {
      xTransform = d3.zoomTransform(overlayX.node());
    }
    if (!yTransform) {
      yTransform = d3.zoomTransform(overlayY.node());
    }

  }


  var line = d3.line()
    .curve(d3.curveMonotoneX)
    .x(function (d) {
      return xScale(d.date);
    })
    .y(function (d) {
      return yScale(d.price);
    });

  svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

  var focus = svg.append("g")
    .attr("class", "focus")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


  var data = getData();
  xScale.domain(d3.extent(data, function (d) {
    return d.date;
  }));
  yScale.domain([0, d3.max(data, function (d) {
    return d.price;
  })]);

  var path = focus.append("path")
    .datum(data)
    .attr("class", "line")
    .attr("d", line);


  var xGroup = focus.append("g")
    .attr("transform", "translate(0," + height + ")").call(xAxis);

  var yGroup = focus.append("g")
    .call(yAxis);

  var overlayX = focus
    .append("rect")
    .attr("fill", "rgba(0,0,0,0.5)")
    .attr("width", width)
    .attr("height", 30)
    .attr("y", height)
    .call(xZoom);


  var overlayY = focus
    .append("rect")
    .attr("fill", "rgba(0,0,0,0.5)")
    .attr("width", 30)
    .attr("height", height)
    .attr("x", -30)
    .call(yZoom);

  var overlayRect = svg.append("rect")
    .attr("class", "zoom")
    .attr("width", width)
    .attr("height", height)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

  function zoomBoth(axisType) {
    getZoomedScales(axisType);
    updateData();
    updateAxes();
  }


  function getZoomedScales(axisType) {
    if (!xOld) {
      xOld = xScale.copy();
    }

    if (!yOld) {
      yOld = yScale.copy();
    }


    var transformXY = d3.zoomTransform(overlayRect.node());
    if (axisType === 'x') {
      xScale = transformXY.rescaleX(xTransform.rescaleX(xOld));
    } else if (axisType === 'y') {
      yScale = transformXY.rescaleY(yTransform.rescaleY(yOld));
    } else {
      xScale = xyTransform.rescaleX(xTransform.rescaleX(xOld));
      yScale = xyTransform.rescaleY(yTransform.rescaleY(yOld));
    }
  }


  function updateAxes() {
    xGroup.call(xAxis.scale(xScale));
    yGroup.call(yAxis.scale(yScale));
  }


  function updateData() {
    focus.select(".line").attr("d", line);
  }

  function getData() {
    var data = [];
    var date = new Date('Jan 2000');
    for (var i = 0; i < 120; i++) {

      data.push({date: new Date(date), price: Math.random() * 100});
      date.setMonth(date.getMonth() + 1);
    }
    return data;
  }

</script>

我该如何解决这个问题?

希望您能理解我的问题并提供帮助。

此致, DW

通过将我的缩放比例从 svg 更改为 Canvas 我已经解决了我的问题。

See https://jsfiddle.net/DaWa/d5fdLwv0/4/