Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating D3 zoom behavior from v3

Tags:

zooming

d3.js

I am upgrading my d3 from version 3 to version 4 and having trouble updating the zoom behavior.

The following returns an error that y is not a function. Any Idea how to write this in version 4?

 d3.zoom()
   .y(yAxis)
   .scaleExtent([1, 20])
   .on('zoom', myFunctionHandler);
like image 477
T Rao Avatar asked Sep 12 '25 13:09

T Rao


1 Answers

D3v3

In d3v3 the zoom behavior optionally modified x and y scales' domain and range using the zoom.x and zoom.y methods. This resulted in the following pattern:

let svg = d3.select("svg");
let g = svg.append("g").attr("transfrom","translate(0,50)");

let scale = d3.scale.linear()
   .range([10,290])
   
let axis = d3.svg.axis()
    .scale(scale)
    .ticks(4)
    .orient("bottom");
    
g.call(axis);

let zoom = d3.behavior.zoom()
                .y(scale)
                .scaleExtent([1, 20])
                .on('zoom', zoomed);

svg.call(zoom);

function zoomed() {
  g.call(axis);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<svg width="300"></svg>

D3v4 and D3v5

D3v4 saw a substantial reworking of D3 as compared with v3, but the zoom behavior saw extensive changes relative to rest of the library. Instead of the zoom behavior modifying scales, the zoom event's transform have rescale methods that can be passed a scale. The rescale methods return a new scale with the appropriate updated domain for any given zoom (for either a horizontal or vertical scale), eg:

d3.event.transform.rescaleX(xScale);

Of course we need to update d3.svg.axis to d3.axis and d3.scale.type to d3.scaleType as well.

This leads to the following pattern:

let svg = d3.select("svg");
let g = svg.append("g").attr("transform","translate(0,50)");

let scale = d3.scaleLinear()
   .range([10,290])
       
let axis = d3.axisBottom()
   .scale(scale)
   .ticks(4);
        
g.call(axis);

let zoom = d3.zoom()
  .on("zoom", zoomed);
  
function zoomed() {
  let zoomedScale = d3.event.transform.rescaleX(scale);
  axis.scale(zoomedScale);
  g.call(axis);
}

svg.call(zoom);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="300"></svg>

Why not just rescale the original scale and use this with the axis? Because the zoom state is relative to its starting state (k: 1, x:0, y:0) we want to always use the original scale when rescaling, so we need to preserve the original scale. See here for more on that.

That the zoom no longer tracks/modifies x or y scales has resulted in the removal of the x and y methods of the zoom behavior: which explains your error that y is not a function.

D3v6 and v7

Starting with d3v6 the d3.event global is removed. Instead the event is passed to the zoom event function directly:

.on("zoom", function(event, datum) { ...

Which means we only need tweak the above:

let svg = d3.select("svg");
let g = svg.append("g").attr("transform","translate(0,50)");

let scale = d3.scaleLinear()
   .range([10,290])
       
let axis = d3.axisBottom()
   .scale(scale)
   .ticks(4);
        
g.call(axis);

let zoom = d3.zoom()
  .on("zoom", zoomed);
  
function zoomed({transform}) {
  let zoomedScale = transform.rescaleX(scale);
  axis.scale(zoomedScale);
  g.call(axis);
}

svg.call(zoom);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
<svg width="300"></svg>

TL;DR

For all versions of d3 after v4 the zoom behavior does not modify scales. Instead the zoom event transform property provides methods to rescale x and y scales, these methods return the new rescaled scales to provide to axis generators.

like image 177
Andrew Reid Avatar answered Sep 16 '25 03:09

Andrew Reid