Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3-zoom mouseup event workaround

Tags:

d3.js

I'm aware that starting with v4 d3-zoom swallows up certain events for some reasons I don't fully understand. I've read some discussions about this, and I know that if I stopPropagation() on mousedown, then the zoom behavior won't get a chance to consume the event, and mouseup will consequently fire. The problem with that is that then the zoom doesnt work.

I haven't found a workaround for the case of needing to handle the mouseup event AND still have the zoom work. I'm particularly interested in the dragging case only. When the user does mousedown and starts to drag the canvas, I want to change the cursor to a clenched-hand, and when the user stops dragging and lets go of the mouse I want to change the cursor back.

How is this possible to do with the new d3-zoom behavior without resorting to a timeout? 'click' event is also not an option since that doesn't fire if there's a mousemove event in between.

like image 338
parliament Avatar asked Feb 13 '20 03:02

parliament


Video Answer


1 Answers

I am concluding from your question that you are not able to track mouseup event after dragging the canvas. If that is the case then we can use events provided by "zoom" functionality - zoomstart and zoomend.

We can simply add that to zoom behavior and track when the user starts zoom and ends zoom. And in this way we can easily change cursor property.

Please find below code snippet and working code as well. Please let me know if i am missing anything.

var margin = {
    top: -5,
    right: -5,
    bottom: -5,
    left: -5
  },
  width = 460 - margin.left - margin.right,
  height = 300 - margin.top - margin.bottom;

var zoom = d3.zoom()
  .scaleExtent([1, 10])
  .on("zoom", zoomed)
  .on("start", function() {
    document.getElementsByTagName("svg")[0].style.cursor = "grab";
  })
  .on("end", function() {
    document.getElementsByTagName("svg")[0].style.cursor = "default";
  })

console.log(zoom.scaleExtent()[0], zoom.scaleExtent()[1]);

var drag = d3.drag()
  .subject(function(d) {
    return d;
  })
  .on("start", dragstarted)
  .on("drag", dragged)
  .on("end", dragended);

var slider = d3.select("body").append("p").append("input")
  .datum({})
  .attr("type", "range")
  .attr("value", zoom.scaleExtent()[0])
  .attr("min", zoom.scaleExtent()[0])
  .attr("max", zoom.scaleExtent()[1])
  .attr("step", (zoom.scaleExtent()[1] - zoom.scaleExtent()[0]) / 100)
  .on("input", slided);

var svg = d3.select("body").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.right + ")")
  .call(zoom);

var rect = svg.append("rect")
  .attr("width", width)
  .attr("height", height)
  .style("fill", "none")
  .style("pointer-events", "all");

var container = svg.append("g");

container.append("g")
  .attr("class", "x axis")
  .selectAll("line")
  .data(d3.range(0, width, 10))
  .enter().append("line")
  .attr("x1", function(d) {
    return d;
  })
  .attr("y1", 0)
  .attr("x2", function(d) {
    return d;
  })
  .attr("y2", height);

container.append("g")
  .attr("class", "y axis")
  .selectAll("line")
  .data(d3.range(0, height, 10))
  .enter().append("line")
  .attr("x1", 0)
  .attr("y1", function(d) {
    return d;
  })
  .attr("x2", width)
  .attr("y2", function(d) {
    return d;
  });

dots = [{
  x: 100,
  y: 100,
}]

dot = container.append("g")
  .attr("class", "dot")
  .selectAll("circle")
  .data(dots)
  .enter().append("circle")
  .attr("r", 5)
  .attr("cx", function(d) {
    return d.x;
  })
  .attr("cy", function(d) {
    return d.y;
  })
  .call(drag);

function dottype(d) {
  d.x = +d.x;
  d.y = +d.y;
  return d;
}

function zoomed(event) {
  const currentTransform = d3.event.transform;
  container.attr("transform", currentTransform);
  slider.property("value", currentTransform.k);
}

function dragstarted(d) {
  d3.event.sourceEvent.stopPropagation();
  d3.select(this).classed("dragging", true);
}

function dragged(d) {
  d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}

function dragended(d) {
  d3.select(this).classed("dragging", false);
}

function slided(d) {
  zoom.scaleTo(svg, d3.select(this).property("value"));
}
.dot circle {
  fill: lightsteelblue;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.dot circle.dragging {
  fill: red;
  stroke: brown;
}

.axis line {
  fill: none;
  stroke: #ddd;
  shape-rendering: crispEdges;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <style>

  </style>
  <title></title>
</head>

<body>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</body>

</html>
        var zoom = d3.zoom()
        .scaleExtent([1, 10])
        .on("zoom", zoomed)
        .on("start", function() {
          document.getElementsByTagName("svg")[0].style.cursor = "grab";
        })
        .on("end", function() {
          document.getElementsByTagName("svg")[0].style.cursor = "default";
        })
like image 164
Jasdeep Singh Avatar answered Oct 13 '22 12:10

Jasdeep Singh