将文本放置在具有 d3 力布局的圆弧上

Place text on an arc with d3 force layout

提问人:gurt 提问时间:11/12/2023 最后编辑:BallpointBengurt 更新时间:11/19/2023 访问量:44

问:

我正在使用 d3 force(使用 sveltekit)创建中心辐射可视化。我使用过链接力、ManyBody 力和中心力。我想为每个节点添加标签,以便文本围绕节点的边缘弯曲。

我想要的输出如下所示:
"Life, physical, & social science" curved around circle

这是代码的相关部分,没有任何标签

        const svg = d3.select('#graph').attr('width', width).attr('height', height);

        // Create the simulation
        const simulation = d3
            .forceSimulation(nodes)
            .force(
                'link',
                d3
                    .forceLink(links)
                    .id((d) => d.name)
                    .distance(linkLength)
            )
            .force('charge', d3.forceManyBody().strength(-100))
            .force('center', d3.forceCenter(width / 2, height / 2));

        // Create links
        const link = svg
            .append('g')
            .attr('stroke', 'black')
            .selectAll('line')
            .data(links)
            .join('line')
            .attr('stroke-width', 2);

        // Create nodes
        const node = svg
            .append('g')
            .attr('stroke', 'black')
            .attr('stroke-width', 2)
            .selectAll('circle')
            .data(nodes)
            .join('circle')
            .attr('r', nodeRadius)
            .attr('fill', 'lightblue');

        simulation.on('tick', () => {
            link
                .attr('x1', (d) => d.source.x)
                .attr('y1', (d) => d.source.y)
                .attr('x2', (d) => d.target.x)
                .attr('y2', (d) => d.target.y);

            node.attr('cx', (d) => d.x).attr('cy', (d) => d.y);
     }

我尝试过像这样创建弯曲标签的标签:

         defs
            .selectAll('.node-text-path')
            .data(nodes)
            .enter()
            .append('path')
            .attr('id', (d) => `text-path-${d.name.replace(/\s/g, '-')}`)
            .attr(
                'd',
                (d) => `
                M ${d.x - nodeRadius}, ${d.y} 
                a ${nodeRadius},${nodeRadius} 0 1,1 ${nodeRadius * 2},0 
                a ${nodeRadius},${nodeRadius} 0 1,1 -${nodeRadius * 2},0
                    `
            ); 

        // Create text elements
        svg
            .selectAll('.node-text')
            .data(nodes)
            .enter()
            .append('text')
            .append('textPath')
            .attr('xlink:href', (d) => `#text-path-${d.name.replace(/\s/g, '-')}`)
            .style('text-anchor', 'middle')
            .attr('startOffset', '50%')
            .text((d) => d.name);

         simulation.on('tick', () => {
            // other code

            defs.selectAll('.node-text-path').attr(
                'd',
                (d) => `
                    M ${d.x - nodeRadius}, ${d.y} 
                    a ${nodeRadius},${nodeRadius} 0 1,1 ${nodeRadius * 2},0 
                    a ${nodeRadius},${nodeRadius} 0 1,1 -${nodeRadius * 2},0
                    `
            );
        });

此解决方案已接近,但存在一些问题

  • 当 svg 初始加载时,标签将显示在 svg 的左上角。更新后,标签会移动到正确的位置。

    enter image description here

    enter image description here

  • 标签不会跟随/更新模拟,这意味着它们的位置有点不稳定

  • 理想情况下,我希望将标签放置在节点下方而不是右侧,但我会让这个滑动

有没有人有任何在具有力导向布局的弯曲路径上使用文本的技巧/经验?

JavaScript d3.js 布局 d3 强制导向

评论

0赞 BallpointBen 11/15/2023
和 和有什么不一样?nodeRadiusnodeRadius(d)
0赞 gurt 11/19/2023
错别字 — 在后期修复。谢谢

答: 暂无答案