首页 > 解决方案 > D3 v6:如何按选定段缩放堆叠条以仅拉伸选定段并缩小其余段(并保持初始图表宽度)?

问题描述

我的水平堆叠条有问题。

问题:有时我得到的值非常小,因此带段之一(子带?)的宽度非常小。(请看下面的图片,我称之为段的每种颜色的矩形):

在此处输入图像描述

在某些情况下,我什至看不到图表上的这一部分。将来我想在每个段上显示文本(百分比值)。但由于段的宽度可能太小,我需要一个显示文本的解决方案。

可能的解决方案:首先我想设置最小的段宽度。但似乎在此之后图表看起来不太好。我也尝试过玩 xScale:

const maxX = 1.4; // this value selected experimentally
const xScale = d3.scaleLinear().range([ 0, width ]).domain([ 0, maxX ]);

但是对于某些情况,段仍然太小​​(请看第三段,我用蓝色标记了它)。

在此处输入图像描述

所以现在我选择按选定段缩放堆叠条的解决方案。例如,我想放大红色段,图表上的所有红色段都应该被拉伸,其余的段应该被缩小。并且带的总宽度应该相同(与初始宽度相同)。所以图表的宽度应该相同。

这是我的代码示例:https ://jsfiddle.net/8d1te7cb/2/

这里的问题是我无法正确缩放选定的片段。我试图检测当前的细分组:

const currentNode = d3.select(event.sourceEvent.target).node();
const currentGroup = d3.select(currentNode.parentNode).node();

然后我尝试仅重新调整与以下相关的段currentGroup

group.selectAll("rect.segment")
                // .attr("transform", event.transform.toString())
                .attr("x", (d, i, n) => {
                    if (n[i].parentNode === currentGroup) {
                        return xScale(d[0]) + PADDING_TO_SHOW_TEXT;
                    }
                    return xScale2(d[0]) + PADDING_TO_SHOW_TEXT;
                })
                .attr("width", (d, i, n) => {
                    if (n[i].parentNode === currentGroup) {
                        return xScale(d[1]) - xScale(d[0])
                    }

                    return xScale2(d[1]) - xScale2(d[0])
                });

但实际上所有段都没有currentGroup保持它们的宽度和选定的组拉伸太多,所以它移到了轴之外(实际上在那之后我改变了图表的宽度)。

问题:如何修复缩放以仅允许选定的组拉伸和其余组收缩(并保持图表的初始宽度)?

额外的问题:是否存在其他按比例显示细分的方式,即使其中一些价值太小?

更新:我在打字稿上的初始项目,所以我忘了删除一些 ts 提示,这是一个更新的示例:https ://jsfiddle.net/8d1te7cb/3/ (从注释代码中删除了 ts)

标签: javascriptd3.js

解决方案


好吧,最后我实现了我想要的。

结果: https ://jsfiddle.net/2yqbvrwp/

详细信息:首先,我决定删除第二个比例,因为我只在运行时需要它。因此,我的缩放功能更改为:

d3.zoom().scaleExtent([ 1, 10 ])
        // I am not sure if I need the line below since code works same without it
        // I think below is default value

        //.translateExtent([ [ 0, 0 ], [ width, height ] ])
        .on("zoom", (event) => {

            const transform = event.transform;

            // the new scale I use for runtime
            // the important part here is clamp method. It prevents from moving
            // segments outside of axis
            const newScaleX = transform.rescaleX(xScale).clamp(true);

            // so I just applied new scale to current axis
            xAxis.scale(newScaleX)
            svg.select("g.axis-x").call(xAxis);

            svg.selectAll("rect.segment")
                .attr("x", (d) => newScaleX(d[0]))
                .attr("width", (d) => newScaleX(d[1]) - newScaleX(d[0]));
        })

    svg.call(zoom);

我还从 css 中删除了rect变换,并在代码中添加了边距。但我想我会把它退回来,因为原生 css 更快。


推荐阅读