Ссылки d3-sankey не обновляются при перетаскивании - PullRequest
1 голос
/ 03 февраля 2020

Проблема-1: Я не могу обновить ссылки d3-sankey для событий перетаскивания. Я пытаюсь реплицировать события перетаскивания d3 по вертикали и горизонтали, подобные этому: https://bl.ocks.org/d3noob/5028304 Когда используется событие перетаскивания, оно обновляет узел, а ссылки с ним не обновляются. Я тоже посмотрел на это https://observablehq.com/@geekplux / dragable-d3-sankey -gram и у меня все еще есть другие проблемы. Мне не удалось скопировать код в jsfiddle, поэтому я делюсь здесь кодом JS.

const d3 : any = require('d3');
const sankey : any = require('d3-sankey');
    const width = 960;
    const height = 500;

    const svg = d3.select('#chart')
              .append('svg')
              .attr('viewBox', [0, 0, width, height]);

    const formatNumber = d3.format(',.0f');
    const format = function (d: any) { return `${formatNumber(d)}TWh`; };
    const color = d3.scaleOrdinal(d3.schemeCategory10);

    const path = d3.sankeyLinkHorizontal();

    const sankeyGraph = d3.sankey()
        .nodeWidth(15)
        .nodePadding(10)
        .size([width, height]);

    const graph = JSON.parse(JSON.stringify({
      nodes: mainData.data.nodes.filter((val:any) => {
        return val.name !== '';
      })
      .map((d : any, i : any) => {
        return Object.assign({}, d, { nodeId : i });
      }),
      links: mainData.data.links.filter((val:any) => {
        return (val.source !== '') || (val.target !== '') || (val.value !== '');
      })
      .map((d : any, i : any) => {
        return Object.assign({}, d, { linkId : i });
      }),
    }));

    sankeyGraph(graph);
    let link = svg.append('g')
        .attr('class', 'links')
        .attr('fill', 'none')
        .attr('stroke', '#000')
        .attr('stroke-opacity', 0.2)
        .selectAll('path');

    let node = svg.append('g')
        .attr('class', 'nodes')
        .attr('font-size', 10)
        .selectAll('g');

    link = link
        .data(graph.links)
        .enter()
        .append('path')
        .attr('d', path) // d3.sankeyLinkHorizontal()
        .attr('stroke-width', (d: any) => { return Math.max(1, d.width); });
        // .on('mouseover', handleMouseOver)
        // .on('mouseout', handleMouseOut);

    link.append('title')
        .text((d: any) => {
          return `${d.source.name} → ${d.target.name} -> ${format(d.value)}`;
        });

    node = node
        .data(graph.nodes)
        .enter()
        .append('g');

    node.append('rect')
        .attr('x', (d: any) => { return d.x0; })
        .attr('y', (d: any) => { return d.y0; })
        .attr('height', (d: any) => { return d.y1 - d.y0; })
        .attr('width', (d: any) => { return d.x1 - d.x0; })
        .attr('fill', (d: any) => { return color(d.name.replace(/ .*/, '')); })
        .attr('stroke', '#000')
        .call(d3.drag()
            .subject((d:any) => d)
            // .on('start', () => d3.event.currenTarget.appendChild(d3.event.currenTarget))
            .on('drag', dragmove));

    function dragmove(d:any, i: number, n: any) { // tslint:disable-line
      console.log(d3.event, d3);
      // console.log('drag', d3.event.sourceEvent.target, d3.event, d3.sankey);
      // const m = d3.select(n[i])
      // .node()
      // .getCTM();
      const x = d3.event.x - d3.event.dx;
      const y = d3.event.y - d3.event.dy;
      // console.log(m, x, y);

      d3.select(n[i])
      .attr('x', x)
      .attr('y', y);
// *********the issue is here********
      d3.sankey(graph)
      .update(graph);
      // d3.sankey()
      // .relayout(graph);
      link.attr('d', path); // d3.sankeyLinkHorizontal() 

    }
    node.append('text')
        .attr('x', (d: any) => { return d.x0 - 6; })
        .attr('y', (d: any) => { return (d.y1 + d.y0) / 2; })
        .attr('dy', '0.35em')
        .attr('text-anchor', 'end')
        .text((d: any) => { return d.name; })
        .filter((d: any) => { return d.x0 < width / 2; })
        .attr('x', (d: any) => { return d.x1 + 6; })
        .attr('text-anchor', 'start');

    node.append('title')
        .text((d: any) => {
          return `${d.name} => ${format(d.value)}`;
        });

Проблема 2: иногда это событие нельзя использовать в Vue -D3. Так что у меня возникли проблемы с кодом события начала перетаскивания. Пожалуйста, предложите альтернативу.

this.parentNode.appendChild(this);

Проблема 3: Кто-нибудь повторил следующее для sankey с d3-V5. Мне нужен поток / ссылка до и после для каждого узла при наведении. http://bl.ocks.org/tomshanley/11277583

Используемые рамки: Vuejs + машинописная версия D3: 5.9.2 Пакет D3 sankey: https://github.com/d3/d3-sankey

mainData JSON:

"data": {
      "nodes": [
        {
          "name": "Agricultural waste"
        },
        {
          "name": "Bio-conversion"
        },
        {
          "name": "Liquid"
        },
        {
          "name": "Losses"
        },
        {
          "name": "Solid"
        },
        {
          "name": "Gas"
        },
        {
          "name": "Biofuel imports"
        },
        {
          "name": "Biomass imports"
        },
        {
          "name": "Coal imports"
        },
        {
          "name": "Coal"
        },
        {
          "name": "Coal reserves"
        },
        {
          "name": "District heating"
        },
        {
          "name": "Industry"
        },
        {
          "name": "Heating and cooling - commercial"
        },
        {
          "name": "Heating and cooling - homes"
        },
        {
          "name": "Electricity grid"
        },
        {
          "name": "Over generation / exports"
        },
        {
          "name": "H2 conversion"
        },
        {
          "name": "Road transport"
        },
        {
          "name": "Agriculture"
        },
        {
          "name": "Rail transport"
        },
        {
          "name": "Lighting & appliances - commercial"
        },
        {
          "name": "Lighting & appliances - homes"
        },
        {
          "name": "Gas imports"
        },
        {
          "name": "Ngas"
        },
        {
          "name": "Gas reserves"
        },
        {
          "name": "Thermal generation"
        },
        {
          "name": "Geothermal"
        },
        {
          "name": "H2"
        },
        {
          "name": "Hydro"
        },
        {
          "name": "International shipping"
        },
        {
          "name": "Domestic aviation"
        },
        {
          "name": "International aviation"
        },
        {
          "name": "National navigation"
        },
        {
          "name": "Marine algae"
        },
        {
          "name": "Nuclear"
        },
        {
          "name": "Oil imports"
        },
        {
          "name": "Oil"
        },
        {
          "name": "Oil reserves"
        },
        {
          "name": "Other waste"
        },
        {
          "name": "Pumped heat"
        },
        {
          "name": "Solar PV"
        },
        {
          "name": "Solar Thermal"
        },
        {
          "name": "Solar"
        },
        {
          "name": "Tidal"
        },
        {
          "name": "UK land based bioenergy"
        },
        {
          "name": "Wave"
        },
        {
          "name": "Wind"
        }
      ],
      "links": [
        {
          "source": 0,
          "target": 1,
          "value": 124.729
        },
        {
          "source": 1,
          "target": 2,
          "value": 0.597
        },
        {
          "source": 1,
          "target": 3,
          "value": 26.862
        },
        {
          "source": 1,
          "target": 4,
          "value": 280.322
        },
        {
          "source": 1,
          "target": 5,
          "value": 81.144
        },
        {
          "source": 6,
          "target": 2,
          "value": 35
        },
        {
          "source": 7,
          "target": 4,
          "value": 35
        },
        {
          "source": 8,
          "target": 9,
          "value": 11.606
        },
        {
          "source": 10,
          "target": 9,
          "value": 63.965
        },
        {
          "source": 9,
          "target": 4,
          "value": 75.571
        },
        {
          "source": 11,
          "target": 12,
          "value": 10.639
        },
        {
          "source": 11,
          "target": 13,
          "value": 22.505
        },
        {
          "source": 11,
          "target": 14,
          "value": 46.184
        },
        {
          "source": 15,
          "target": 16,
          "value": 104.453
        },
        {
          "source": 15,
          "target": 14,
          "value": 113.726
        },
        {
          "source": 15,
          "target": 17,
          "value": 27.14
        },
        {
          "source": 15,
          "target": 12,
          "value": 342.165
        },
        {
          "source": 15,
          "target": 18,
          "value": 37.797
        },
        {
          "source": 15,
          "target": 19,
          "value": 4.412
        },
        {
          "source": 15,
          "target": 13,
          "value": 40.858
        },
        {
          "source": 15,
          "target": 3,
          "value": 56.691
        },
        {
          "source": 15,
          "target": 20,
          "value": 7.863
        },
        {
          "source": 15,
          "target": 21,
          "value": 90.008
        },
        {
          "source": 15,
          "target": 22,
          "value": 93.494
        },
        {
          "source": 23,
          "target": 24,
          "value": 40.719
        },
        {
          "source": 25,
          "target": 24,
          "value": 82.233
        },
        {
          "source": 5,
          "target": 13,
          "value": 0.129
        },
        {
          "source": 5,
          "target": 3,
          "value": 1.401
        },
        {
          "source": 5,
          "target": 26,
          "value": 151.891
        },
        {
          "source": 5,
          "target": 19,
          "value": 2.096
        },
        {
          "source": 5,
          "target": 12,
          "value": 48.58
        },
        {
          "source": 27,
          "target": 15,
          "value": 7.013
        },
        {
          "source": 17,
          "target": 28,
          "value": 20.897
        },
        {
          "source": 17,
          "target": 3,
          "value": 6.242
        },
        {
          "source": 28,
          "target": 18,
          "value": 20.897
        },
        {
          "source": 29,
          "target": 15,
          "value": 6.995
        },
        {
          "source": 2,
          "target": 12,
          "value": 121.066
        },
        {
          "source": 2,
          "target": 30,
          "value": 128.69
        },
        {
          "source": 2,
          "target": 18,
          "value": 135.835
        },
        {
          "source": 2,
          "target": 31,
          "value": 14.458
        },
        {
          "source": 2,
          "target": 32,
          "value": 206.267
        },
        {
          "source": 2,
          "target": 19,
          "value": 3.64
        },
        {
          "source": 2,
          "target": 33,
          "value": 33.218
        },
        {
          "source": 2,
          "target": 20,
          "value": 4.413
        },
        {
          "source": 34,
          "target": 1,
          "value": 4.375
        },
        {
          "source": 24,
          "target": 5,
          "value": 122.952
        },
        {
          "source": 35,
          "target": 26,
          "value": 839.978
        },
        {
          "source": 36,
          "target": 37,
          "value": 504.287
        },
        {
          "source": 38,
          "target": 37,
          "value": 107.703
        },
        {
          "source": 37,
          "target": 2,
          "value": 611.99
        },
        {
          "source": 39,
          "target": 4,
          "value": 56.587
        },
        {
          "source": 39,
          "target": 1,
          "value": 77.81
        },
        {
          "source": 40,
          "target": 14,
          "value": 193.026
        },
        {
          "source": 40,
          "target": 13,
          "value": 70.672
        },
        {
          "source": 41,
          "target": 15,
          "value": 59.901
        },
        {
          "source": 42,
          "target": 14,
          "value": 19.263
        },
        {
          "source": 43,
          "target": 42,
          "value": 19.263
        },
        {
          "source": 43,
          "target": 41,
          "value": 59.901
        },
        {
          "source": 4,
          "target": 19,
          "value": 0.882
        },
        {
          "source": 4,
          "target": 26,
          "value": 400.12
        },
        {
          "source": 4,
          "target": 12,
          "value": 46.477
        },
        {
          "source": 26,
          "target": 15,
          "value": 525.531
        },
        {
          "source": 26,
          "target": 3,
          "value": 787.129
        },
        {
          "source": 26,
          "target": 11,
          "value": 79.329
        },
        {
          "source": 44,
          "target": 15,
          "value": 9.452
        },
        {
          "source": 45,
          "target": 1,
          "value": 182.01
        },
        {
          "source": 46,
          "target": 15,
          "value": 19.013
        },
        {
          "source": 47,
          "target": 15,
          "value": 289.366
        }
      ]
    }

Спасибо за ваши решения заранее.

function sg(mainData) {
    const width = 960;
    const height = 500;

    const svg = d3.select('#chart')
              .append('svg')
              .attr('viewBox', [0, 0, width, height]);

    const formatNumber = d3.format(',.0f');
    const format = function (d: any) { return `${formatNumber(d)}TWh`; };
    const color = d3.scaleOrdinal(d3.schemeCategory10);

    const path = d3.sankeyLinkHorizontal();

    const sankeyGraph = d3.sankey()
        .nodeWidth(15)
        .nodePadding(10)
        .size([width, height]);

    const graph = JSON.parse(JSON.stringify({
      nodes: mainData.data.nodes.filter((val:any) => {
        return val.name !== '';
      })
      .map((d : any, i : any) => {
        return Object.assign({}, d, { nodeId : i });
      }),
      links: mainData.data.links.filter((val:any) => {
        return (val.source !== '') || (val.target !== '') || (val.value !== '');
      })
      .map((d : any, i : any) => {
        return Object.assign({}, d, { linkId : i });
      }),
    }));

    sankeyGraph(graph);
    let link = svg.append('g')
        .attr('class', 'links')
        .attr('fill', 'none')
        .attr('stroke', '#000')
        .attr('stroke-opacity', 0.2)
        .selectAll('path');

    let node = svg.append('g')
        .attr('class', 'nodes')
        .attr('font-size', 10)
        .selectAll('g');

    link = link
        .data(graph.links)
        .enter()
        .append('path')
        .attr('d', path) // d3.sankeyLinkHorizontal()
        .attr('stroke-width', (d: any) => { return Math.max(1, d.width); });
        // .on('mouseover', handleMouseOver)
        // .on('mouseout', handleMouseOut);

    link.append('title')
        .text((d: any) => {
          return `${d.source.name} → ${d.target.name} -> ${format(d.value)}`;
        });

    node = node
        .data(graph.nodes)
        .enter()
        .append('g');

    node.append('rect')
        .attr('x', (d: any) => { return d.x0; })
        .attr('y', (d: any) => { return d.y0; })
        .attr('height', (d: any) => { return d.y1 - d.y0; })
        .attr('width', (d: any) => { return d.x1 - d.x0; })
        .attr('fill', (d: any) => { return color(d.name.replace(/ .*/, '')); })
        .attr('stroke', '#000')
        .call(d3.drag()
            .subject((d:any) => d)
            // .on('start', () => d3.event.currenTarget.appendChild(d3.event.currenTarget))
            .on('drag', dragmove));

    function dragmove(d:any, i: number, n: any) { // tslint:disable-line
      console.log(d3.event, d3);
      // console.log('drag', d3.event.sourceEvent.target, d3.event, d3.sankey);
      // const m = d3.select(n[i])
      // .node()
      // .getCTM();
      const x = d3.event.x - d3.event.dx;
      const y = d3.event.y - d3.event.dy;
      // console.log(m, x, y);

      d3.select(n[i])
      .attr('x', x)
      .attr('y', y);
// *********the issue is here********
      d3.sankey(graph)
      .update(graph);
      // d3.sankey()
      // .relayout(graph);
      link.attr('d', path); // d3.sankeyLinkHorizontal() 

    }
    node.append('text')
        .attr('x', (d: any) => { return d.x0 - 6; })
        .attr('y', (d: any) => { return (d.y1 + d.y0) / 2; })
        .attr('dy', '0.35em')
        .attr('text-anchor', 'end')
        .text((d: any) => { return d.name; })
        .filter((d: any) => { return d.x0 < width / 2; })
        .attr('x', (d: any) => { return d.x1 + 6; })
        .attr('text-anchor', 'start');

    node.append('title')
        .text((d: any) => {
          return `${d.name} => ${format(d.value)}`;
        });
}
const data = {
  "data": {
    "nodes": [{
        "name": "Agricultural waste"
      },
      {
        "name": "Bio-conversion"
      },
      {
        "name": "Liquid"
      },
      {
        "name": "Losses"
      },
      {
        "name": "Solid"
      },
      {
        "name": "Gas"
      },
      {
        "name": "Biofuel imports"
      },
      {
        "name": "Biomass imports"
      },
      {
        "name": "Coal imports"
      },
      {
        "name": "Coal"
      },
      {
        "name": "Coal reserves"
      },
      {
        "name": "District heating"
      },
      {
        "name": "Industry"
      },
      {
        "name": "Heating and cooling - commercial"
      },
      {
        "name": "Heating and cooling - homes"
      },
      {
        "name": "Electricity grid"
      },
      {
        "name": "Over generation / exports"
      },
      {
        "name": "H2 conversion"
      },
      {
        "name": "Road transport"
      },
      {
        "name": "Agriculture"
      },
      {
        "name": "Rail transport"
      },
      {
        "name": "Lighting & appliances - commercial"
      },
      {
        "name": "Lighting & appliances - homes"
      },
      {
        "name": "Gas imports"
      },
      {
        "name": "Ngas"
      },
      {
        "name": "Gas reserves"
      },
      {
        "name": "Thermal generation"
      },
      {
        "name": "Geothermal"
      },
      {
        "name": "H2"
      },
      {
        "name": "Hydro"
      },
      {
        "name": "International shipping"
      },
      {
        "name": "Domestic aviation"
      },
      {
        "name": "International aviation"
      },
      {
        "name": "National navigation"
      },
      {
        "name": "Marine algae"
      },
      {
        "name": "Nuclear"
      },
      {
        "name": "Oil imports"
      },
      {
        "name": "Oil"
      },
      {
        "name": "Oil reserves"
      },
      {
        "name": "Other waste"
      },
      {
        "name": "Pumped heat"
      },
      {
        "name": "Solar PV"
      },
      {
        "name": "Solar Thermal"
      },
      {
        "name": "Solar"
      },
      {
        "name": "Tidal"
      },
      {
        "name": "UK land based bioenergy"
      },
      {
        "name": "Wave"
      },
      {
        "name": "Wind"
      }
    ],
    "links": [{
        "source": 0,
        "target": 1,
        "value": 124.729
      },
      {
        "source": 1,
        "target": 2,
        "value": 0.597
      },
      {
        "source": 1,
        "target": 3,
        "value": 26.862
      },
      {
        "source": 1,
        "target": 4,
        "value": 280.322
      },
      {
        "source": 1,
        "target": 5,
        "value": 81.144
      },
      {
        "source": 6,
        "target": 2,
        "value": 35
      },
      {
        "source": 7,
        "target": 4,
        "value": 35
      },
      {
        "source": 8,
        "target": 9,
        "value": 11.606
      },
      {
        "source": 10,
        "target": 9,
        "value": 63.965
      },
      {
        "source": 9,
        "target": 4,
        "value": 75.571
      },
      {
        "source": 11,
        "target": 12,
        "value": 10.639
      },
      {
        "source": 11,
        "target": 13,
        "value": 22.505
      },
      {
        "source": 11,
        "target": 14,
        "value": 46.184
      },
      {
        "source": 15,
        "target": 16,
        "value": 104.453
      },
      {
        "source": 15,
        "target": 14,
        "value": 113.726
      },
      {
        "source": 15,
        "target": 17,
        "value": 27.14
      },
      {
        "source": 15,
        "target": 12,
        "value": 342.165
      },
      {
        "source": 15,
        "target": 18,
        "value": 37.797
      },
      {
        "source": 15,
        "target": 19,
        "value": 4.412
      },
      {
        "source": 15,
        "target": 13,
        "value": 40.858
      },
      {
        "source": 15,
        "target": 3,
        "value": 56.691
      },
      {
        "source": 15,
        "target": 20,
        "value": 7.863
      },
      {
        "source": 15,
        "target": 21,
        "value": 90.008
      },
      {
        "source": 15,
        "target": 22,
        "value": 93.494
      },
      {
        "source": 23,
        "target": 24,
        "value": 40.719
      },
      {
        "source": 25,
        "target": 24,
        "value": 82.233
      },
      {
        "source": 5,
        "target": 13,
        "value": 0.129
      },
      {
        "source": 5,
        "target": 3,
        "value": 1.401
      },
      {
        "source": 5,
        "target": 26,
        "value": 151.891
      },
      {
        "source": 5,
        "target": 19,
        "value": 2.096
      },
      {
        "source": 5,
        "target": 12,
        "value": 48.58
      },
      {
        "source": 27,
        "target": 15,
        "value": 7.013
      },
      {
        "source": 17,
        "target": 28,
        "value": 20.897
      },
      {
        "source": 17,
        "target": 3,
        "value": 6.242
      },
      {
        "source": 28,
        "target": 18,
        "value": 20.897
      },
      {
        "source": 29,
        "target": 15,
        "value": 6.995
      },
      {
        "source": 2,
        "target": 12,
        "value": 121.066
      },
      {
        "source": 2,
        "target": 30,
        "value": 128.69
      },
      {
        "source": 2,
        "target": 18,
        "value": 135.835
      },
      {
        "source": 2,
        "target": 31,
        "value": 14.458
      },
      {
        "source": 2,
        "target": 32,
        "value": 206.267
      },
      {
        "source": 2,
        "target": 19,
        "value": 3.64
      },
      {
        "source": 2,
        "target": 33,
        "value": 33.218
      },
      {
        "source": 2,
        "target": 20,
        "value": 4.413
      },
      {
        "source": 34,
        "target": 1,
        "value": 4.375
      },
      {
        "source": 24,
        "target": 5,
        "value": 122.952
      },
      {
        "source": 35,
        "target": 26,
        "value": 839.978
      },
      {
        "source": 36,
        "target": 37,
        "value": 504.287
      },
      {
        "source": 38,
        "target": 37,
        "value": 107.703
      },
      {
        "source": 37,
        "target": 2,
        "value": 611.99
      },
      {
        "source": 39,
        "target": 4,
        "value": 56.587
      },
      {
        "source": 39,
        "target": 1,
        "value": 77.81
      },
      {
        "source": 40,
        "target": 14,
        "value": 193.026
      },
      {
        "source": 40,
        "target": 13,
        "value": 70.672
      },
      {
        "source": 41,
        "target": 15,
        "value": 59.901
      },
      {
        "source": 42,
        "target": 14,
        "value": 19.263
      },
      {
        "source": 43,
        "target": 42,
        "value": 19.263
      },
      {
        "source": 43,
        "target": 41,
        "value": 59.901
      },
      {
        "source": 4,
        "target": 19,
        "value": 0.882
      },
      {
        "source": 4,
        "target": 26,
        "value": 400.12
      },
      {
        "source": 4,
        "target": 12,
        "value": 46.477
      },
      {
        "source": 26,
        "target": 15,
        "value": 525.531
      },
      {
        "source": 26,
        "target": 3,
        "value": 787.129
      },
      {
        "source": 26,
        "target": 11,
        "value": 79.329
      },
      {
        "source": 44,
        "target": 15,
        "value": 9.452
      },
      {
        "source": 45,
        "target": 1,
        "value": 182.01
      },
      {
        "source": 46,
        "target": 15,
        "value": 19.013
      },
      {
        "source": 47,
        "target": 15,
        "value": 289.366
      }
    ]
  }
}
sg(data)
<script src="https://cdnjs.cloudflare.com/ajax/libs/typescript/3.7.5/typescript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-sankey/0.7.1/d3-sankey.min.js"></script>
<div id="chart"></div>

enter image description here

...