首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >更新D3v7和弦布局

更新D3v7和弦布局
EN

Stack Overflow用户
提问于 2022-09-09 08:11:05
回答 1查看 75关注 0票数 1

最近,我正在尝试理解D3和弦图,特别是D3v7。我得到了两个不同的数据集,并希望动态更新这些数据集。一般来说,它除了两个问题外,还能发挥作用:

  1. 每个数据集中的第一个对象将被忽略。我不知道为什么,因为任何console.log都显示了现有的数据,但是它根本没有绘制。例如,基于groups1和paths1的dataset 1缺少对象A,在dataset2中缺少对象E。

  1. 在更新图形后,旧元素仍然存在,图形在不删除旧路径的情况下创建新路径。每次单击和更改都会增加浏览器中的D3元素,而不是替换它们.

因为D3v3和D3v7有一点变化,所以我想不出足够快的办法来帮助自己。

代码语言:javascript
复制
////////////////////////////////////////////////////////////
        ////////////////////// Set-Up Data /////////////////////////
        ////////////////////////////////////////////////////////////
        
        let groups1 = [
            { name: "A", color: "blue" },
            { name: "B", color: "red" },
            { name: "C", color: "green" },
            { name: "D", color: "yellow" },
        ]

        let paths1 = [
            { from: groups1.name = "A", to: groups1.name = "A", strength: 0 },
            { from: groups1.name = "A", to: groups1.name = "B", strength: 5 },
            { from: groups1.name = "A", to: groups1.name = "C", strength: 5 },
            { from: groups1.name = "A", to: groups1.name = "D", strength: 5 },
            { from: groups1.name = "B", to: groups1.name = "A", strength: 5 },
            { from: groups1.name = "B", to: groups1.name = "B", strength: 0 },
            { from: groups1.name = "B", to: groups1.name = "C", strength: 5 },
            { from: groups1.name = "B", to: groups1.name = "D", strength: 5 },
            { from: groups1.name = "C", to: groups1.name = "A", strength: 5 },
            { from: groups1.name = "C", to: groups1.name = "B", strength: 5 },
            { from: groups1.name = "C", to: groups1.name = "C", strength: 0 },
            { from: groups1.name = "C", to: groups1.name = "D", strength: 5 },
            { from: groups1.name = "D", to: groups1.name = "D", strength: 0 },
            { from: groups1.name = "D", to: groups1.name = "A", strength: 5 },
            { from: groups1.name = "D", to: groups1.name = "B", strength: 5 },
            { from: groups1.name = "D", to: groups1.name = "C", strength: 5 },
        ]


        let groups2 = [
            { name: "E", color: "#301E1E" },
            { name: "F", color: "#083E77" },
            { name: "G", color: "#342350" },
        ]

        let paths2 = [
            { from: groups2.name = "E", to: groups2.name = "E", strength: 0 },
            { from: groups2.name = "E", to: groups2.name = "F", strength: 5 },
            { from: groups2.name = "E", to: groups2.name = "G", strength: 5 },
            { from: groups2.name = "F", to: groups2.name = "E", strength: 5 },
            { from: groups2.name = "F", to: groups2.name = "F", strength: 0 },
            { from: groups2.name = "F", to: groups2.name = "G", strength: 5 },
            { from: groups2.name = "G", to: groups2.name = "E", strength: 5 },
            { from: groups2.name = "G", to: groups2.name = "F", strength: 5 },
            { from: groups2.name = "G", to: groups2.name = "G", strength: 0 },
        ]

        let chord = []
        let matrix = []

        function getMatrix(paths, groups) {
            matrix = []

            var mapPaths = paths.map(item => {
                const container = {}
                    container.from = groups.findIndex(ele => ele.name == item.from)
                    container.to = groups.findIndex(ele => ele.name == item.to)
                    container.strength = item.strength
                return container
            })
            
            mapPaths.forEach(function (item) {
                // initialize sub-arra if not yet exists
                if (!matrix[item.to]) {
                    matrix[item.to] = []
                }

                matrix[item.to][item.from] = item.strength
            })

            return matrix
        }

        // --- --- --- --- --- ---

        const vw = document.documentElement.clientWidth / 2
        const vh = document.documentElement.clientHeight / 2

        let innerRadius = Math.min(vw, vh) * 0.5 
        let outerRadius = innerRadius * 1.1;
        
        let svg = d3.select("#chart").append("svg")
            .attr("width", vw)
            .attr("height", vh)
            .attr("overflow", "unset")  

        var chordGenerator = d3.chord()
            .padAngle(0.10)
            .sortSubgroups(d3.ascending)
            .sortChords(d3.descending)

        chord = chordGenerator(getMatrix(paths1, groups1))

        var arc = d3.arc()
            .innerRadius(innerRadius * 1.01)
            .outerRadius(outerRadius)

        var ribbon = d3.ribbon()
            .radius(innerRadius);
    
        window.update = update;

        update(paths1, groups1)

        function update(thisPaths, thisGroups) {
            let groups = thisGroups
            let paths = thisPaths

            getMatrix(thisPaths, thisGroups)

            const chords = chordGenerator(matrix);

            let wrapper = svg.append("g")
                .attr("transform", "translate(" + vw / 2 + "," + vh / 2 + ")")

            let ribbons = wrapper.append("g");
            
            let g = wrapper.selectAll("g")
                    .data(chord.groups)
                    .enter()
                    .append("g")
                    .attr("class", "group")

            const ribbonsUpdate = ribbons.selectAll("path")
                .data(chords, ({ source, target }) => source.index + '-' + target.index)

            const duration = 1000;

            ribbonsUpdate
                .transition()
                .duration(duration)
                .attr("d", ribbon)
                .style("fill", function (d) { return groups[d.target.index].color; })
                
            ribbonsUpdate
                .enter()
                .append("path")
                .attr("opacity", 0)
                .attr("d", ribbon)
                .style("fill", function (d) { return groups[d.target.index].color; })
                .transition()
                .duration(duration)
                .attr('opacity', 1)
                
            // adding Arc Blocks
            g.append("path")
                .style("fill", function (d) { return groups[d.index].color; })
                .attr("d", arc)
                .style("opacity", 1)

            // adding labels
            g.append("text")
                .each(function(d){ d.angle = (d.startAngle + d.endAngle) / 2; })
                .attr("dy", ".35em")
                .attr("class", "titles")
                .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
                .attr("transform", function(d) {
                    return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                    + "translate(" + (outerRadius + 10) + ")"
                    + (d.angle > Math.PI ? "rotate(180)" : "");
                })
                .text(function(d,i){ return groups[i].name; })

            ribbonsUpdate
                .exit()
                .transition()
                .duration(duration)
                .attr("opacity", 0)
                .remove();
        }

        document.getElementById('data1').onclick = () => {
            chord = chordGenerator(getMatrix(paths1, groups1))
            update(paths1, groups1);
        }

        document.getElementById('data2').onclick = () => {
            chord = chordGenerator(getMatrix(paths2, groups2))
            update(paths2, groups2);
        }
代码语言:javascript
复制
body {
        font-size: 16px;
        font-family: 'Oswald', sans-serif;
        text-align: center;
        background-color: #ECF0F3;
        cursor: default;
        overflow: hidden;
    }
代码语言:javascript
复制
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <title>Animated_D3v7</title>

    <!-- jQuery -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    
    <!-- D3.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>

<style>
    body {
        font-size: 16px;
        font-family: 'Oswald', sans-serif;
        background-color: #ECF0F3;
        cursor: default;
        overflow: hidden;
    }

</style>

<body>
    <button id="data1">data 1</button>
    <button id="data2">data 2</button>
    
    <div id="chart"></div>
    
</body>

</html>

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-13 10:36:09

问题1通过以下方式得到解决:

代码语言:javascript
复制
let g = wrapper.selectAll("g.group") // add class of 'group' to the selector

否则,所选内容有一些模糊性,因为您已经在前面的语句中向g添加了一个wrapperlet ribbons = wrapper.append("g");

问题2似乎涉及得更多一些。

首先,每次update被称为g时,在svg下创建一个新的g并分配给wrapper。您只需一次就可以在wrapper之外定义update

其次,update使用chordchords,前者在update之外更新,后者在update中更新。您可以不使用前者,只需在chordmatrix中更新update

第三,您可以为外部的每个弧、缎带和标签设置一个g,然后进入、更新和退出逻辑针对它们中的pathtext元素工作。

在下面的工作示例中,我简化了其他几点:

代码语言:javascript
复制
const vw = document.documentElement.clientWidth / 2
const vh = document.documentElement.clientHeight / 2
const innerRadius = Math.min(vw, vh) * 0.4 
const outerRadius = innerRadius * 1.1;
const duration = 1000;

const svg = d3.select("#chart").append("svg")
    .attr("width", vw)
    .attr("height", vh)

const wrapper = svg.append("g")
    .attr("transform", "translate(" + vw / 2 + "," + vh / 2 + ")")
    
const ribbonsG = wrapper.append("g").attr("id", "ribbons")
const arcsG = wrapper.append("g").attr("id", "arcs")
const labelsG = wrapper.append("g").attr("id", "labels")
    
const chordGenerator = d3.chord()
    .padAngle(0.10)
    .sortSubgroups(d3.ascending)
    .sortChords(d3.descending)

const arc = d3.arc()
    .innerRadius(innerRadius * 1.01)
    .outerRadius(outerRadius)

const ribbon = d3.ribbon()
    .radius(innerRadius);
    
update(paths1, groups1)

function getMatrix(paths, groups) {
    matrix = []

    var mapPaths = paths.map(item => {
        const container = {}
            container.from = groups.findIndex(ele => ele.name == item.from)
            container.to = groups.findIndex(ele => ele.name == item.to)
            container.strength = item.strength
        return container
    })

    mapPaths.forEach(function (item) {
        // initialize sub-arra if not yet exists
        if (!matrix[item.to]) {
            matrix[item.to] = []
        }

        matrix[item.to][item.from] = item.strength
    })

    return matrix
}

function update(thisPaths, thisGroups) {
    const groups = thisGroups
    const paths = thisPaths
    const chords = chordGenerator(getMatrix(thisPaths, thisGroups));

    // ribbons
    const ribbonsUpdate = ribbonsG
        .selectAll("path.ribbon")
        .data(chords, ({ source, target }) => source.index + '-' + target.index)
    
    const ribbonsEnter = ribbonsUpdate
        .enter()
        .append("path")
        
    ribbonsUpdate
        .merge(ribbonsEnter)
        .attr("opacity", 0)
        .attr("class", "ribbon")        
        .transition()
        .duration(duration)
        .attr("d", ribbon)
        .style("fill", function (d) { return groups[d.target.index].color; })
        .attr('opacity', 1)

    ribbonsUpdate
        .exit()
        .transition()
        .duration(duration)
        .attr("opacity", 0)
        .remove();
        
    // arcs
    const arcsUpdate = arcsG
        .selectAll("path.arc")
        .data(chords.groups)

    const arcsEnter = arcsUpdate
        .enter()
        .append("path")

    arcsUpdate
        .merge(arcsEnter)
        .attr("opacity", 0)
        .attr("class", "arc")
        .transition()
        .duration(duration)
        .attr("d", arc)
        .style("fill", function (d) { return groups[d.index].color; })
        .attr('opacity', 1)

    arcsUpdate
        .exit()
        .transition()
        .duration(duration)
        .attr("opacity", 0)
        .remove();

    // adding labels
    const labelsUpdate = labelsG
        .selectAll("text.titles")
        .data(chords.groups)

    const labelsEnter = labelsUpdate
        .enter()
        .append("text")

    labelsUpdate
        .merge(labelsEnter)
        .attr("class", "titles")
        .attr("opacity", 0)
        .transition()
        .duration(duration)
        .each(function(d){ d.angle = (d.startAngle + d.endAngle) / 2; })
        .attr("dy", ".35em")
        .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
        .attr("transform", function(d) {
            return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                + "translate(" + (outerRadius + 10) + ")"
                + (d.angle > Math.PI ? "rotate(180)" : "");
        })
        .text(function(d,i){ return groups[i].name; })        
        .attr("opacity", 1)
        
    labelsUpdate
        .exit()
        .remove()
}

document.getElementById('data1').onclick = () => {
    update(paths1, groups1);
}

document.getElementById('data2').onclick = () => {
    update(paths2, groups2);
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.6.1/d3.min.js" charset="utf-8"></script>
<div id="chart"></div>
<button id="data1">data 1</button>
<button id="data2">data 2</button>
<script>
let groups1 = [
    { name: "A", color: "blue" },
    { name: "B", color: "red" },
    { name: "C", color: "green" },
    { name: "D", color: "yellow" },
]

let paths1 = [
    { from: groups1.name = "A", to: groups1.name = "A", strength: 0 },
    { from: groups1.name = "A", to: groups1.name = "B", strength: 5 },
    { from: groups1.name = "A", to: groups1.name = "C", strength: 5 },
    { from: groups1.name = "A", to: groups1.name = "D", strength: 5 },
    { from: groups1.name = "B", to: groups1.name = "A", strength: 5 },
    { from: groups1.name = "B", to: groups1.name = "B", strength: 0 },
    { from: groups1.name = "B", to: groups1.name = "C", strength: 5 },
    { from: groups1.name = "B", to: groups1.name = "D", strength: 5 },
    { from: groups1.name = "C", to: groups1.name = "A", strength: 5 },
    { from: groups1.name = "C", to: groups1.name = "B", strength: 5 },
    { from: groups1.name = "C", to: groups1.name = "C", strength: 0 },
    { from: groups1.name = "C", to: groups1.name = "D", strength: 5 },
    { from: groups1.name = "D", to: groups1.name = "D", strength: 0 },
    { from: groups1.name = "D", to: groups1.name = "A", strength: 5 },
    { from: groups1.name = "D", to: groups1.name = "B", strength: 5 },
    { from: groups1.name = "D", to: groups1.name = "C", strength: 5 },
]


let groups2 = [
    { name: "E", color: "#301E1E" },
    { name: "F", color: "#083E77" },
    { name: "G", color: "#11AA99" },
]

let paths2 = [
    { from: groups2.name = "E", to: groups2.name = "E", strength: 0 },
    { from: groups2.name = "E", to: groups2.name = "F", strength: 5 },
    { from: groups2.name = "E", to: groups2.name = "G", strength: 5 },
    { from: groups2.name = "F", to: groups2.name = "E", strength: 5 },
    { from: groups2.name = "F", to: groups2.name = "F", strength: 0 },
    { from: groups2.name = "F", to: groups2.name = "G", strength: 5 },
    { from: groups2.name = "G", to: groups2.name = "E", strength: 5 },
    { from: groups2.name = "G", to: groups2.name = "F", strength: 5 },
    { from: groups2.name = "G", to: groups2.name = "G", strength: 0 },
]
</script>

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73659174

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档