user7521838
user7521838

Reputation: 370

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

I am trying to build an trend with d3 that should be zoomable. That is working very fine. It can be zoomed inside the trend (zooms all axes) and by each axis via mouse wheel.

Now I wanted to add some zooms by button click. These zooms are also working fine.

Zooming in X direction by button and then zooming in Y direction or trend via mouse wheel works perfect.

But after zooming by button in X direction and afterwoods zooming via mousewheel in X direction the trend starts to zoom from default.

Here is my example: 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>

How can I solve that problem?

Upvotes: 3

Views: 1095

Answers (1)

user7521838
user7521838

Reputation: 370

By changing my zoom from svg to Canvas I already solved my problem.

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

Upvotes: 1

Related Questions