[英]How to proper zoom and pan only the y-axis in D3 v5 y-axis
我正在使用 d3 v6 开发可视化,它基本上使用一组条目来显示水平层。 可视化如下所示:
需要注意的是,随着层数的增加,一些层变得不可能看到适当的规模减少。 所以我想制作一个缩放(和平移)功能,但它只能在 Y 轴上工作。 我搜索了很多并找到了本教程( https://observablehq.com/@d3/xy-zoom ),从中我做了一些更改以适应我的可视化:
const svgHeight: any = d3.select(svgContainer.current).attr('height');
const yScaleGlobal = d3
.scaleLinear()
.domain([0, maxYValues])
.range([0, svgHeight - MARGINS.TOP - MARGINS.BOTTOM]);
const yAxis = d3.axisLeft(yScaleGlobal).tickFormat((d: any) => `${d} m`);
const gY = layerGroup.append('g').attr('class', 'y-axis');
// z holds a copy of the previous transform, so we can track its changes
let z = d3.zoomIdentity;
// set up the ancillary zooms and an accessor for their transforms
const zoomY = d3.zoom().scaleExtent([0.2, 5]);
const ty = () => d3.zoomTransform(gY.node());
gY.call(zoomY).attr('pointer-events', 'none');
drawViz();
const zoom = d3.zoom().on('zoom', function (e) {
const t = e.transform;
const k = t.k / z.k;
const point = center(e, this);
if (k === 1) {
// pure translation?
gY.call(zoomY.translateBy, 0, (t.y - z.y) / ty().k);
} else {
// if not, we're zooming on a fixed point
gY.call(zoomY.scaleBy, k, point);
}
z = t;
drawViz();
});
svg.call(zoom).call(zoom.transform, d3.zoomIdentity.scale(0.8)).node();
function drawViz() {
const yr = ty().rescaleY(yScaleGlobal);
yAxis.scale(yr);
gY.call(yAxis, yr);
if (data) updateLayers(data, yr);
}
function center(event, target) {
if (event.sourceEvent) {
const p = d3.pointers(event, target);
return [d3.mean(p, (d) => d[0]), d3.mean(p, (d) => d[1])];
}
return [svgWidth / 2, svgHeight / 2];
}
updateLayers
函数是这样的:
const updateLayers = (data: LAYER_COMPONENT_TYPE[], yScale) => {
const layerFill = getLayerFill(data);
const rects = layerGroup.selectAll('rect').data(data);
rects.exit().remove();
const newLayers = rects
.enter()
.append('rect')
.attr('x', 10)
.style('opacity', 0.5)
.attr('width', 300)
.style('stroke', '#101010')
.style('stroke-width', '1px');
newLayers
.merge(rects)
.style('fill', (d: LAYER_COMPONENT_TYPE) => {
if (!layerFill[`${d.fgdc_texture}.${d.from}`].url) {
return layerFill[`${d.fgdc_texture}.${d.from}`];
}
svg.call(layerFill[`${d.fgdc_texture}.${d.from}`]);
return layerFill[`${d.fgdc_texture}.${d.from}`].url();
})
.attr('y', (d: LAYER_COMPONENT_TYPE, i) => {
if (i === 0) return yScale(d.from);
return yScale(data[i - 1].to);
})
.attr('height', (d: LAYER_COMPONENT_TYPE, i) => {
return yScale(d.to - d.from);
});
};
缩放和平移工作正常,整个可视化也很好,但 yScale 有一个奇怪的行为。 矩形的高度在缩放或平移时超出比例(我已经意识到高度的异常等于 yScale 处“0 m”点的距离) here you can view the problem
无论如何,我可以弄清楚代码有什么问题或导致异常的原因。 所以我需要一些帮助来解决这个问题
编辑 1:我正在使用 react btw
我已经提出了一个解决方案(使用更好更清洁的方法)。 该解决方案基于 TBD 的d3 时间线实现。 使用他的解决方案,我只是适应了 y 轴和我的代码
const maxYValues = d3.max(maxValues) || 0;
const yScaleGlobal = d3
.scaleLinear()
.domain([0, maxYValues])
.range([0, svgHeight - MARGINS.TOP - MARGINS.BOTTOM]);
const yAxis = d3.axisLeft(yScaleGlobal).tickFormat((d: any) => `${d} m`);
const gY = litoligicalGroup.select(`.${styles.yAxis}`).call(yAxis);
const spanY = (d) => {
if (d.thickness) return yScaleGlobal(0) - 10;
return yScaleGlobal(d.from);
};
const spanH = (d) => {
if (d.thickness) return 10;
return yScaleGlobal(d.to - d.from);
};
const zoom = d3
.zoom()
.scaleExtent([0.2, 5])
.on('zoom', (e) => {
const transform = e.transform;
gY.call(yAxis.scale(transform.rescaleY(yScaleGlobal)));
// the viz group is where all the layers are attached
vizGroup
.selectAll('rect')
.attr('y', (d) => {
if (!d) return null;
return transform.applyY(spanY(d));
})
.attr('height', (d) => {
if (!d) return null;
return transform.k * spanH(d);
});
});
drawProfile();
svg.call(zoom);
function drawProfile() {
if (geologicData) updateGeology(geologicData, yScaleGlobal);
if (constructionData) updatePoco(constructionData, yScaleGlobal);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.