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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With