Union find
Union find

Reputation: 8150

d3 zoom to bounding box II scrolling while zooming throws error

I am throwing an error on my implementation of Mike Bostock's zoom to bounding box II.

The error stops the zoom mid-way and locks up the map if you scroll during the zooming event.

Any ideas on fixing? The error is present on Bostock's example.

Here is a screenshot of the error:

enter image description here

The code:

var width = 500,
    height = 370,
    active = d3.select(null);

var projection = d3.geo.albersUsa()
    .scale(675)
    .translate([width / 2, height / 2]);

var path = d3.geo.path()
    .projection(projection);

var svg = d3.select(".app-map")
    .append("svg")
      .attr("width", width)
      .attr("height", height);

var g = svg.append("g")
      .attr("class", "app-counties-map");

function ready(error, us) {

  g.selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
  .enter().append("path")
    .attr("class", "app-counties")
    .attr("d", path)
    .on("click",clicked);

  function clicked(d) {
    if (active.node() === this) return reset();
    active.classed("active", false);
    active = d3.select(this).classed("active", true);

    var bounds = path.bounds(d),
      dx = bounds[1][0] - bounds[0][0],
      dy = bounds[1][3] - bounds[0][4],
      x = (bounds[0][0] + bounds[1][0]) / 2,
      y = (bounds[0][5] + bounds[1][6]) / 2,
      scale = 3.5,
      translate = [width / 2 - scale * x, height / 2 - scale * y];

    svg.transition()
      .duration(450)
      .call(zoom.translate(translate).scale(scale).event);
  };

  function reset() {
    active.classed("active", false);
    active = d3.select(null);

    svg.transition()
      .duration(450)
      .call(zoom.translate([0, 0]).scale(1).event);
  };

  var zoom = d3.behavior.zoom()
    .translate([0, 0])
    .scale(1)
    .scaleExtent([1, 8])
    .on("zoom", zoomed);

  svg.on("click", stopped, true);

  svg
    .call(zoom)
    .call(zoom.event);

  function zoomed() {
    g.style("stroke-width", 1.5 / d3.event.scale + "px");
    g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
  }

  function stopped() {
    if (d3.event.defaultPrevented) d3.event.stopPropagation();
  }

}


queue()
    .defer(d3.json, "./data/us (2).json")
    .await(ready);

Upvotes: 0

Views: 545

Answers (1)

Andrew Yoo
Andrew Yoo

Reputation: 46

I believe that the problem is happening because the mouse wheel event is trying to zoom while the zoom transition animation from click event is still playing ( I didn't get the error in chrome but in IE ).

You could turn the mouse-wheel event off at the beginning of the click event and enable it back on after the animation/transition finish.

In order to wait for the transition to finish, I've used .each, but maybe .call can be used as well.

Added 20150501 8:24PM PST

I am off work now so I can explain in more detail.

jsfiddle example

1. Disabling mouse scroll zoom event

var _wheelZoomEvent = svg.on("wheel.zoom");
function clicked(d) {

        svg.on("wheel.zoom", null);

While click transition is executing, we want to prevent the mouse wheel event from triggering another zoom transition. svg.on("wheel.zoom") is returning the reference to the event handler, so that I can reattach when the transition is complete. svg.on("wheel.zoom", null) detaches the event handler from mousewheel event.

2. Adding callback at the end of transition

After the transition completes we need to re-attach the mousewheel event back to the svg object.

svg.transition()
            .duration(750)
            .call(zoom.translate([0, 0]).scale(1).event)
            .each("end", function(){ 
                  svg.on("wheel.zoom", _wheelZoomEvent);
            });

There is another transition inside reset() function, and we need to attach event back on there as well.

Upvotes: 1

Related Questions