使用 javascript、d3 和来自 json 文件的数据在圆弧中制作径向渐变的问题

Problems making radial gradient in arcs using javascript, d3 with data from json file

我正在使用 javascript 和 d3 (v6) 并努力制作 sunburst type plot 或者至少是 arcs at different radial distances where each arc has potentially its own gradient (a radial gradient from center to periphery). At this point I've followed some examples (e.g. here, ) where I set up a set of radialGradient elements and then use the ids from that to generate the fill in my actual arcs. But I am generating my arcs and gradients from data from a json file, and this makes it different from previous questions because the svg element generation is within the d3.json call (e.g. here 的情节)。我有两个问题。 (1) 渐变设置有这样的行:

grads.append("stop")
        .attr("offset", "0%").style("stop-color", "white")
        .attr("offset", "100%").style("stop-color", "green");

但这些似乎并没有真正添加到 grads 元素中(当我查看网页中的检查器时)。
(2) 圆弧的 link 似乎不起作用,尽管这可能是因为第一个问题。

JAVASCRIPT(可以查到here

var width = window.innerWidth,
    height = window.innerHeight;

var body = d3.select('body')

// append svg to the DIV
chart = d3.select(".chart");

const svg = chart.append("svg:svg")
    .attr("width", width)
    .attr("height", height);

///////////////////////////////////////  Global variables controlling the arc appearance //////////////////
const arcMin = 30;
const arcWidth = 45.5;
const arcPad = 1;
///////////////////////////////////////////////////////////////////////////////////////////////

d3.json('dataExample.json')
    .then(function (data) {

        const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences);
        grads.enter().append("radialGradient")
            .attr("gradientUnits", "objectBoundingBox")
            .attr("cx", 0)
            .attr("cy", 0)
            .attr("fr", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            .attr("r", (d, i) => arcMin + d.pulse * (arcWidth))
            .attr("id", function (d) {
                return "grad" + d.code;
            });
        grads.append("stop")
            .attr("offset", "0%").style("stop-color", "white")
            .attr("offset", "100%").style("stop-color", "green");//eventually this gradient will go between two colors that are functions of the data that is read in from the json file

        console.log(grads);

        var arc = svg.selectAll('path.arc-path')
            .data(data.sequences);
        arc.enter()
            .append('svg:path')
            .attr('d', d3.arc()
                .innerRadius((d, i) => arcMin + (d.pulse - 1) * (arcWidth) + arcPad)
                .outerRadius((d, i) => arcMin + d.pulse * (arcWidth))
                .startAngle(function (d, i) {
                    ang = (i * 30) * Math.PI / 180;
                    return ang;
                })
                .endAngle(function (d, i) {
                    ang = ((i + 1) * 30) * Math.PI / 180;
                    return ang;
                })
            )
            .attr("class", ".arc-path") // assigns a class for easier selecting
            .attr("transform", "translate(600,300)") 
            //.style('fill',(d) => `rgb(${d.code * 10},${d.code*20},${255 -d.code * 7})`); this works - but doesn't use the gradients
            .style("fill", function (d) {return "url(#grad" + d.code + ")";})


    })

JSONFILE(上面叫dataExample.json)可以找到here

{"type":"sequenceData","sequences":[{"pulse":1,"code":0},{"pulse":1,"code":1},{"pulse":1,"code":2},{"pulse":2,"code":3},{"pulse":2,"code":4},{"pulse":2,"code":5},{"pulse":2,"code":6},{"pulse":2,"code":7},{"pulse":2,"code":8},{"pulse":2,"code":9},{"pulse":2,"code":10},{"pulse":3,"code":12}]}

index.html(可以查到here

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Gradient arc test</title>
    </style>
    <script type="text/javascript" src="https://d3js.org/d3.v6.min.js"></script>
  </head>
  <body>
    <div class="chart"></div>
        <script src="arcExample.js">    </script>
  </body>
</html>

如果您注意到其他问题或不良编码习惯,请告诉我,因为我是新手。 谢谢。

(已编辑)

根据 Ruben Helsloot 的评论,我对各个部分做了一些 jsfiddle。有4个小提琴。它们在两个方面有所不同:(1) json 文件的读入方式和 (2) 填充的生成方式。我会注意到,为了我的最终目的,我需要能够从文件中读取 json 文件 - 本地文件或 URL
(1) jsfiddle Version 1 尝试从 URL 中读取 json 并尝试使用渐变进行填充。我认为读取 url 以及渐变填充有问题。
(2) jsfiddle Version 2 尝试从 URL 中读取 json 但不使用渐变进行填充。这在读取 URL 时有问题。当我在本地机器上执行此操作时,从本地文件读取 json,此版本会生成输出(尽管不是渐变)。
(3) jsfiddle Version 3 这将 json 放在一个名为 data 的局部变量中,然后使用它。它还尝试使用渐变进行填充。
(4) jsfiddle Version 4 这会将 json 放入一个名为 data 的局部变量中,然后使用它。它不使用渐变进行填充。这是 4 个中唯一一个在 jsfiddle 上给出输出的。

您应该先学习基本的 SVG 1.1 规范,然后再学习如何将元素(而不是属性)添加到正确的 SVG 结构中。

下面是固定的代码,虽然我不知道你到底想实现什么渐变。您可以尝试将 gradientUnits 属性更改回 objectBoundingBox 但首先还要尝试使用 r 属性.

var width = window.innerWidth,
    height = window.innerHeight;

var body = d3.select('body')

// append svg to the DIV
chart = d3.select(".chart");

const svg = chart.append("svg:svg")
    .attr("width", width)
    .attr("height", height)
    .attr("xmlns", "http://www.w3.org/2000/svg")
    .attr("xmlns:xlink", "http://www.w3.org/1999/xlink");

///////////////////////////////////////  Global variables controlling the arc appearance //////////////////
const arcMin = 30;
const arcWidth = 45.5;
const arcPad = 1;
///////////////////////////////////////////////////////////////////////////////////////////////

var data = {"type":"sequenceData","sequences":[{"pulse":1,"code":0},{"pulse":1,"code":1},{"pulse":1,"code":2},{"pulse":2,"code":3},{"pulse":2,"code":4},{"pulse":2,"code":5},{"pulse":2,"code":6},{"pulse":2,"code":7},{"pulse":2,"code":8},{"pulse":2,"code":9},{"pulse":2,"code":10},{"pulse":3,"code":12}]};

        const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences);
        gradsWrap = grads.enter().append("radialGradient")
            //.attr("gradientUnits", "objectBoundingBox")
            .attr("gradientUnits", "userSpaceOnUse")
            .attr("cx", 0)
            .attr("cy", 0)
            //.attr("fr", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            //.attr("r", (d, i) => arcMin + (d.pulse-1) * (arcWidth))
            .attr("fr", "0%")
            .attr("r", "25%")
            .attr("id", function (d) {
                return "grad" + d.code;
            })
        gradsWrap.append("stop")
            .attr("offset", "0%")
            .attr("stop-color", "white");
        gradsWrap.append("stop")
            .attr("offset", "100%").attr("stop-color", "green");//eventually this gradient will go between two colors that are functions of the data that is read in from the json file

        console.log(grads);

        var arc = svg.selectAll('path.arc-path')
            .data(data.sequences);
        arc.enter()
            .append('svg:path')
            .attr('d', d3.arc()
                .innerRadius((d, i) => arcMin + (d.pulse - 1) * (arcWidth) + arcPad)
                .outerRadius((d, i) => arcMin + d.pulse * (arcWidth))
                .startAngle(function (d, i) {
                    ang = (i * 30) * Math.PI / 180;
                    return ang;
                })
                .endAngle(function (d, i) {
                    ang = ((i + 1) * 30) * Math.PI / 180;
                    return ang;
                })
            )
            .attr("class", ".arc-path") // assigns a class for easier selecting
            .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
            .attr('stroke', 'white')
            .style('stroke-width', '2px')
            .attr("fill", function (d) {
              //return "red";
              return "url(#grad" + d.code + ")";
            })
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <title>Gradient arc test</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>

<body>
  <div class="chart"></div>
</body>