Проблемы с созданием радиального градиента в дугах с использованием javascript, d3 с данными из файла json

Я использую javascript и d3 (v6) и работаю над созданием графика типа солнечных лучей или в как минимум график с дугами на разных радиальных расстояниях, где каждая дуга потенциально имеет свой собственный градиент ( радиальный градиент от центра к периферии). На данный момент я следовал некоторым примерам (например, здесь, здесь), где я устанавливаю набор элементов радиального градиента, а затем использую полученные идентификаторы для создания заливки в моих реальных дугах. Но я генерирую свои дуги и градиенты из данных из файла json, и это отличает его от предыдущих вопросов, потому что генерация элемента svg находится в вызове d3.json (например, здесь). У меня две проблемы. (1) Настройки градиента имеют такие строки, как:

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

но они, кажется, на самом деле не добавляются к элементам градации (когда я смотрю на инспектор на веб-странице).
(2) Ссылка на дуги, похоже, не работает, хотя, возможно, это потому, что первой проблемы.

JAVASCRIPT (можно найти здесь)

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) можно найти здесь

{"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 (можно найти здесь)

<!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>

И если вы заметили другие проблемы или неправильные методы кодирования, сообщите мне, так как я новичок в этом. Спасибо.

(отредактировано)

Основываясь на комментарии Рубена Хелслута, я сделал несколько jsfiddles с различными частями. Есть 4 скрипки. Они различаются по двум аспектам: (1) как считывается файл json и (2) как генерируется заливка. Отмечу, что для моей конечной цели мне нужно иметь возможность читать файл json из файла - либо локального, либо из URL-адреса
(1) jsfiddle Version 1 Пытается прочитать json из URL-адреса и пытается использовать градиенты для заливки. Я думаю, что есть проблема со чтением из URL, а также проблема с градиентной заливкой.
(2) jsfiddle Version 2 Пытается прочитать json из URL-адреса, но не использует градиенты для заливки. Это имеет проблемы с чтением URL. Когда я делаю это на своем локальном компьютере, читая json из локального файла, эта версия генерирует выходные данные (хотя и не градиенты).
(3) jsfiddle Version 3 Это помещает json в локальную переменную с именем data и затем использует ее. Он также пытается использовать градиенты для заливки.
(4) jsfiddle Version 4 Это помещает json в локальную переменную с именем data и затем использует ее. Он не использует градиенты для заливки. Это единственный из 4, который выводит на jsfiddle.


person sar    schedule 18.11.2020    source источник
comment
Превратите свой код в работоспособный минимально воспроизводимый пример, это поможет нам понять, что происходит. ВЫ можете разместить свой файл JSON на github или просто сохранить его как переменную data и удалить вызов d3.json   -  person Ruben Helsloot    schedule 18.11.2020
comment
Я отредактировал пост со ссылками на 4 jsfiddles с вариантами, которые показывают проблему. Возникла проблема при чтении URL-адреса с использованием d3.json. В конечном счете, мне нужно иметь возможность читать из файла или URL-адреса вместо того, чтобы иметь данные в самом файле javascript. Я также разместил файлы index.html, arcExample.js и dataExample.json в gitlab (ссылки в вопросе) для локального тестирования.   -  person sar    schedule 18.11.2020


Ответы (1)


Сначала вы должны изучить базовую спецификацию 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>

person Matt Sergej Rinc    schedule 18.11.2020
comment
Основываясь на вашем коде и примере, на котором я основывал свой код здесь, я также попытался удалить точку с запятой после const grads = svg.append("defs").selectAll("radialGradient").data(data.sequences) и сразу перейти к .enter().append("radialGradient")... часть вместо добавления дополнительной переменной gradsWrap, и это тоже сработало. Это многое проясняет. - person sar; 18.11.2020
comment
Конечно, вам просто нужно различать, добавляете ли вы атрибуты или подэлементы к одному элементу SVG. В последнем случае, когда часто требуется несколько подэлементов, целесообразно определить обертывающий (переменный) элемент. - person Matt Sergej Rinc; 19.11.2020