I am using d3 v4 (4.12.0).
I have an SVG container in which I draw a simple horizontal axis (X axis, linear scale) that responds to mouse panning.
I would like to simulate an “infinite” or “infinite” horizontal axis.
By this, I mean that I only want to load and display a small part of a very large dataset and only draw an axis that shows a very small subset of the elements from this large set.
Say I have a horizontal axis that shows 10 data points from a larger array of objects. I hold an offset parameter that starts at 0 to show the first ten points of this array.
My procedure:
When I scroll the axis to the left far enough to show the 11th and subsequent data point, I then:
Update the offset parameter to display how many units I have converted.
Update x-axis scale based on new offset value
Redraw axis labels with updated scale range ( x_scale )
Translate the group element containing the axis by the number of pixels that represent one unit on the axis ( scroller_element_width )
My attempt works until step 3. This process seems to fail in step 4, since the final axis translation never happens.
The entire axis moves to the left, and it has fresh labels, but it does not move to the right with these updated labels - it basically falls from the page.
I would like to ask d3 experts why this step fails and what I can do to fix it.
Here is a function that draws an axis and attaches a zoom event:
renderScroller() { console.log("renderScroller called"); if ((this.state.scrollerWidth == 0) || (this.state.scrollerHeight == 0)) return; const self = this; const scroller = this.scrollerContainer; const scroller_content = this.scrollerContent; const scroller_width = this.state.scrollerWidth; const scroller_height = this.state.scrollerHeight; var offset = 0, limit = 10, current_index = 10; var min_translate_x = 0, max_translate_x; var scroller_data = Constants.test_data.slice(offset, limit); var x_extent = d3.extent(scroller_data, function(d) { return d.window; }); var y_extent = [0, d3.max(scroller_data, function(d) { return d.total; })]; var x_scale = d3.scaleLinear(); var y_scale = d3.scaleLinear(); var x_axis_call = d3.axisTop(); x_scale.domain(x_extent).range([0, scroller_width]); y_scale.domain(y_extent).range([scroller_height, 0]); x_axis_call.scale(x_scale); d3.select(scroller_content) .append("g") .attr("class", "x axis") .attr("transform", "translate(" + [0, scroller_height] + ")") .call(x_axis_call); var scroller_element_width = parseFloat(scroller_width / (x_scale.domain()[1] - x_scale.domain()[0])); var pan = d3.zoom() .on("zoom", function () { var t = parseSvg(d3.select(scroller_content).attr("transform")); var x_offset = parseFloat((t.translateX + d3.event.transform.x) / scroller_element_width);
This is a function inside the React component. React stuff is not that important, but here is the render() function of this component to show the parent elements of the SVG and child group:
render() { return ( <svg className="scroller" ref={(scroller) => { this.scrollerContainer = scroller; }} width={this.state.scrollerWidth} height={this.state.scrollerHeight}> <g className="scroller-content" ref={(scrollerContent) => { this.scrollerContent = scrollerContent; }} /> </svg> ); }
As shown, scrollerContainer ref is an SVG containing an element of the scrollerContent group. This scrollerContent is what contains the horizontal axis.
When panning or scrolling scrollerContent X axis, transformations are applied to scrollerContent .
To get the conversion parameters, I use the helper method parseSvg from d3-interpolate , i.e. through ES6:
import * as d3 from 'd3'; import { parseSvg } from "d3-interpolate/src/transform/parse";
For completeness, here is a snippet of test data:
export const test_data = [ { "total": 29.86, "signal": [ 4.842, 1.608, 1.837, 3.052, 1.677, 0.8041, 3.09, 1.813, 2.106, 2.38, 1.773, 0.8128, 2.047, 1.658, 0.3588 ], "window": 0, "chr": "chr1" }, { "total": 35.67, "signal": [ 0.6111, 1.995, 0.5715, 2.51, 3.318, 1.523, 3.94, 2.743, 4.445, 0.759, 4.938, 2.61, 3.379, 1.27, 1.057 ], "window": 1, "chr": "chr1" }, { "total": 39.14, "signal": [ 0.0589, 0.1608, 2.426, 4.673, 3.511, 3.912, 2.809, 4.197, 4.648, 2.069, 2.84, 3.878, 0.2681, 3.622, 0.06911 ], "window": 2, "chr": "chr1" }, { "total": 37.45, "signal": [ 2.688, 1.235, 2.358, 1.994, 1.541, 1.189, 0.8078, 4.872, 2.287, 4.266, 2.24, 3.349, 3.519, 1.896, 3.21 ], "window": 3, "chr": "chr1" }, { "total": 47.17, "signal": [ 3.338, 3.613, 3.872, 1.166, 1.828, 4.24, 1.476, 4.025, 4.144, 4.922, 2.183, 2.701, 3.825, 4.346, 1.494 ], "window": 4, "chr": "chr1" }, { "total": 41.7, "signal": [ 0.2787, 1.74, 0.7557, 4.236, 2.865, 4.542, 4.113, 1.265, 4.826, 3.731, 4.931, 2.392, 2.014, 0.6566, 3.352 ], "window": 5, "chr": "chr1" }, { "total": 31.43, "signal": [ 3.025, 4.399, 1.001, 4.859, 0.9173, 2.851, 2.916, 1.821, 1.228, 1.646, 0.1008, 2.09, 2.502, 0.1476, 1.924 ], "window": 6, "chr": "chr1" }, { "total": 38.23, "signal": [ 1.123, 1.972, 0.5079, 4.808, 0.5669, 4.647, 2.598, 1.874, 0.8699, 4.876, 3.981, 1.503, 4.683, 2.853, 1.366 ], "window": 7, "chr": "chr1" }, { "total": 44.2, "signal": [ 3.895, 0.7457, 2.208, 1.837, 3.219, 3.98, 3.494, 4.225, 3.117, 3.162, 3.171, 2.449, 0.1419, 3.745, 4.807 ], "window": 8, "chr": "chr1" }, { "total": 36.33, "signal": [ 0.3164, 2.753, 4.094, 2.237, 4.748, 2.483, 1.541, 4.113, 0.1874, 3.71, 1.313, 0.221, 2.736, 1.208, 4.671 ], "window": 9, "chr": "chr1" }, { "total": 43.05, "signal": [ 1.924, 0.4136, 3.057, 4.686, 1.263, 0.1333, 0.8786, 4.715, 4.845, 4.282, 2.112, 4.597, 3.822, 1.322, 4.999 ], "window": 10, "chr": "chr1" }, { "total": 31.28, "signal": [ 4.216, 0.6655, 2.078, 1.235, 0.5526, 1.556, 1.005, 3.196, 1.907, 4.932, 0.006601, 1.269, 3.964, 4.608, 0.09109 ], "window": 11, "chr": "chr1" }, { "total": 48.3, "signal": [ 4.469, 1.138, 3.958, 2.801, 3.404, 4.988, 2.649, 3.818, 3.284, 0.9281, 3.982, 0.496, 4.28, 3.258, 4.845 ], "window": 12, "chr": "chr1" }, { "total": 42.1, "signal": [ 1.087, 3.127, 0.493, 3.276, 4.195, 1.561, 2.638, 4.897, 3.675, 4.937, 0.05847, 4.272, 2.33, 1.776, 3.776 ], "window": 13, "chr": "chr1" }, { "total": 40.1, "signal": [ 1.275, 4.574, 2.805, 1.646, 0.8759, 4.948, 3.637, 3.227, 2.259, 2.983, 2.905, 4.134, 3.133, 0.08384, 1.617 ], "window": 14, "chr": "chr1" }, { "total": 50.31, "signal": [ 2.228, 0.7037, 4.977, 1.143, 2.506, 4.348, 4.344, 3.998, 4.213, 2.745, 4.374, 3.411, 4.504, 4.417, 2.396 ], "window": 15, "chr": "chr1" }, { "total": 34.7, "signal": [ 2.729, 3.891, 3.873, 2.973, 0.1487, 1.573, 1.781, 2.788, 2.191, 2.912, 1.355, 2.582, 2.374, 3.164, 0.3641 ], "window": 16, "chr": "chr1" }, { "total": 32.89, "signal": [ 3.619, 2.119, 1.854, 4.083, 0.9916, 0.5065, 0.8343, 4.835, 1.723, 3.926, 2.675, 2.281, 0.1531, 2.239, 1.049 ], "window": 17, "chr": "chr1" }, { "total": 38.94, "signal": [ 1.976, 1.587, 3.808, 0.1173, 3.823, 4.349, 3.652, 1.308, 3.434, 3.855, 1.622, 0.2916, 2.382, 3.091, 3.647 ], "window": 18, "chr": "chr1" }, { "total": 34.18, "signal": [ 0.339, 3.695, 3.108, 3.267, 0.08282, 3.53, 2.316, 1.11, 4.504, 4.111, 0.007636, 0.5581, 2.985, 1.707, 2.857 ], "window": 19, "chr": "chr1" }, { "total": 29.62, "signal": [ 2.695, 0.8477, 4.417, 3.012, 2.454, 2.686, 0.6529, 0.2275, 1.052, 0.2092, 2.968, 3.268, 0.7144, 0.4441, 3.973 ], "window": 20, "chr": "chr1" } ];
Hope this shows all the work needed to explain the problem. Thanks for any tips or advice.