The snippet below creates a single x axis with starting ticks
of 10. During zoom I'm updating ticks on the rescaled axis with:
.ticks(startTicks * Math.floor(event.transform.k))
With .scaleExtent([1, 50])
I can get down from years to 3-hourly blocks fairly smoothly (besides a little label overlap here and there).
But, when I request the number of ticks applied on the scale (xScale.ticks().length
) I get a different number to the one I just assigned.
Also, when I get the labels (xScale.ticks().map(xScale.tickFormat())
) they differ from the ones rendered as I get deeper into the zoom.
Reading here:
An optional count argument requests more or fewer ticks. The number of ticks returned, however, is not necessarily equal to the requested count. Ticks are restricted to nicely-rounded values (multiples of 1, 2, 5 and powers of 10), and the scale’s domain can not always be subdivided in exactly count such intervals. See d3.ticks for more details.
I understand I might not get the number of ticks
I request, but it's counter-intuitive that:
- I request more and more ticks (per
k
) - between 10 and 500 - Then the returned
ticks
fluctuates between 5 and 19.
Why is this ? Is there a better or 'standard' way to update ticks
whilst zooming for scaleTime
or scaleUtc
?
var margin = {top: 0, right: 25, bottom: 20, left: 25}
var width = 600 - margin.left - margin.right;
var height = 40 - margin.top - margin.bottom;
// x domain
var x = d3.timeDays(new Date(2020, 00, 01), new Date(2025, 00, 01));
// start with 10 ticks
var startTicks = 10;
// zoom function
var zoom = d3.zoom()
.on("zoom", (event) => {
var t = event.transform;
xScale
.domain(t.rescaleX(xScale2).domain())
.range([0, width].map(d => t.applyX(d)));
var zoomedRangeWidth = xScale.range()[1] - xScale.range()[0];
var zrw = zoomedRangeWidth.toFixed(4);
var kAppliedToWidth = kw = t.k * width;
var kw = kAppliedToWidth.toFixed(4);
var zoomTicks = zt = startTicks * Math.floor(t.k);
svg.select(".x-axis")
.call(d3.axisBottom(xScale)
.ticks(zt)
);
var realTicks = rt = xScale.ticks().length;
console.log(`zrw: ${zrw}, kw: ${kw}, zt: ${zt}, rt: ${rt}`);
console.log(`labels: ${xScale.ticks().map(xScale.tickFormat())}`);
})
.scaleExtent([1, 50]);
// x scale
var xScale = d3.scaleTime()
.domain(d3.extent(x))
.range([0, width]);
// x scale copy
var xScale2 = xScale.copy();
// svg
var svg = d3.select("#scale")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// clippath
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("width", width)
.attr("height", height);
// x-axis
svg.append("g")
.attr("class", "x-axis")
.attr("clip-path", "url(#clip)")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale)
.ticks(startTicks));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js"></script>
<div id="scale"></div>
from Setting ticks on a time scale during zoom
No comments:
Post a Comment