将D3.js多节点、多链接转换为新版本

问题描述 投票:0回答:1

我请求您为我正在开发的工具提供帮助。 在全球范围内,我找到了我正在寻找的东西(https://jsfiddle.net/4xt5v51m/3/),但问题是它似乎与我的 D3.js 版本(7.8.5)不兼容。

我尝试将其转换为新版本,但我不能...我总是一个接一个地出现一个错误,我认为我的代码不再相关...

javascript d3.js
1个回答
0
投票

通常这样的问题应该被关闭(你需要展示你的工作/转换尝试),但因为我已经做了几次这个练习......这是你的开始:

<!DOCTYPE html>

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.js"></script>
    <style>
      path.link {
        fill: #ccc;
        stroke: #333;
        stroke-width: 1.5px;
      }

      circle {
        fill: #ccc;
        stroke: #333;
        stroke-width: 1.5px;
      }

      text {
        font: 10px sans-serif;
        pointer-events: none;
      }

      text.shadow {
        stroke: #fff;
        stroke-width: 3px;
        stroke-opacity: 0.8;
      }

      body {
        background-color: white;
        margin: 0px;
      }

      .graphContainer {
        text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white,
          1px 1px 0 white;
      }
    </style>
  </head>

  <body>
    <div class="graphContainer"></div>
    <script>
      var color = d3.scaleOrdinal(d3.schemeCategory10);
      var data = {
        nodes: [
          { id: 0, name: 'paper1', citation: 5, group: 1 },
          { id: 1, name: 'paper2', citation: 8, group: 2 },
          { id: 2, name: 'paper3', citation: 12, group: 3 },
          { id: 3, name: 'paper4', citation: 25, group: 4 },
          { id: 4, name: 'paper5', citation: 15, group: 5 },
          { id: 5, name: 'paper6', citation: 5, group: 1 },
          { id: 6, name: 'paper7', citation: 8, group: 2 },
          { id: 7, name: 'paper8', citation: 12, group: 3 },
          { id: 8, name: 'paper9', citation: 25, group: 4 },
          { id: 9, name: 'paper10', citation: 15, group: 5 },
        ],
        links: [
          { source: 0, target: 1, name: 'A-B-1', value: 8, grouo: 1 },
          { source: 0, target: 1, name: 'A-B-2', value: 24, grouo: 2 },
          { source: 0, target: 2, name: 'A-C-1', value: 100, grouo: 1 },
          { source: 0, target: 2, name: 'A-C-3', value: 44, grouo: 2 },
          { source: 2, target: 3, name: 'A-D-1', value: 169, grouo: 1 },
          { source: 2, target: 3, name: 'A-D-2', value: 80, grouo: 2 },
          { source: 2, target: 4, name: 'A-E-1', value: 16, grouo: 1 },
          { source: 2, target: 4, name: 'A-E-5', value: 200, grouo: 2 },
          { source: 4, target: 5, name: 'A-B-1', value: 8, grouo: 1 },
          { source: 4, target: 5, name: 'A-B-2', value: 24, grouo: 2 },
          { source: 5, target: 6, name: 'A-C-1', value: 12, grouo: 1 },
          { source: 5, target: 6, name: 'A-C-3', value: 44, grouo: 2 },
          { source: 5, target: 7, name: 'A-D-1', value: 125, grouo: 1 },
          { source: 5, target: 7, name: 'A-D-2', value: 225, grouo: 2 },
          { source: 7, target: 8, name: 'A-E-1', value: 36, grouo: 1 },
          { source: 7, target: 8, name: 'A-E-5', value: 81, grouo: 2 },
          { source: 8, target: 3, name: 'A-C-1', value: 9, grouo: 1 },
          { source: 8, target: 3, name: 'A-C-3', value: 16, grouo: 2 },
          { source: 8, target: 9, name: 'A-D-1', value: 50, grouo: 1 },
          { source: 8, target: 9, name: 'A-D-2', value: 100, grouo: 2 },
        ],
      };

      // used to store the number of links between two nodes.
      // mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
      var mLinkNum = {};

      // sort links first
      sortLinks();

      // set up linkIndex and linkNumer, because it may possible multiple links share the same source and target node
      setLinkIndexAndNum();

      var w = 960,
        h = 500;

      const force = d3
        .forceSimulation(data.nodes)
        .force(
          'link',
          d3
            .forceLink(data.links)
            .id((d) => d.id)
            .distance(50)
        )
        .force('charge', d3.forceManyBody().strength(-1000))
        .force('center', d3.forceCenter(w / 2, h / 2));

      var svg = d3
        .select('.graphContainer')
        .append('svg:svg')
        .attr('width', w)
        .attr('height', h);

      svg
        .append('svg:defs')
        .selectAll('marker')
        .data(['end']) // Different link/path types can be defined here
        .enter()
        .append('svg:marker') // This section adds in the arrows
        .attr('id', String)
        .attr('viewBox', '0 -5 10 10')
        .attr('refX', 15)
        .attr('refY', 0.5)
        .attr('markerWidth', 2)
        .attr('markerHeight', 2)
        .attr('orient', 'auto')
        .append('svg:path')
        .attr('d', 'M0,-5L10,0L0,5');

      var path = svg
        .append('svg:g')
        .selectAll('line')
        .data(data.links)
        .enter()
        .append('svg:path')
        .attr('class', 'link')
        .style('stroke-width', function (d) {
          return Math.sqrt(d.value);
        })
        .style('stroke', function (d) {
          return color(d.value);
        })
        .attr('marker-end', 'url(#end)');

      var circle = svg
        .append('svg:g')
        .selectAll('circle')
        .data(data.nodes)
        .enter()
        .append('svg:circle')
        .attr('r', function (d) {
          return d.citation;
        })
        .style('fill', function (d) {
          return color(d.group);
        });

      var text = svg
        .append('svg:g')
        .selectAll('g')
        .data(data.nodes)
        .enter()
        .append('svg:g');

      // A copy of the text with a thick white stroke for legibility.
      text
        .append('svg:text')
        .attr('x', 8)
        .attr('y', '.31em')
        .attr('class', 'shadow')
        .text(function (d) {
          return d.name;
        });

      text
        .append('svg:text')
        .attr('x', 8)
        .attr('y', '.31em')
        .text(function (d) {
          return d.name;
        });

      force.on('tick', tick);

      // Use elliptical arc path segments to doubly-encode directionality.
      function tick() {
        path.attr('d', function (d) {
          var dx = d.target.x - d.source.x,
            dy = d.target.y - d.source.y,
            dr = Math.sqrt(dx * dx + dy * dy);
          // get the total link numbers between source and target node
          var lTotalLinkNum =
            mLinkNum[d.source.id + ',' + d.target.id] ||
            mLinkNum[d.target.id + ',' + d.source.id];
          if (lTotalLinkNum > 1) {
            // if there are multiple links between these two nodes, we need generate different dr for each path
            dr = dr / (1 + (1 / lTotalLinkNum) * (d.linkindex - 1));
          }
          // generate svg path
          return (
            'M' +
            d.source.x +
            ',' +
            d.source.y +
            'A' +
            dr +
            ',' +
            dr +
            ' 0 0 1,' +
            d.target.x +
            ',' +
            d.target.y +
            'A' +
            dr +
            ',' +
            dr +
            ' 0 0 0,' +
            d.source.x +
            ',' +
            d.source.y
          );
        });

        // Add tooltip to the connection path
        path.append('svg:title').text(function (d, i) {
          return d.name;
        });

        circle.attr('transform', function (d) {
          return 'translate(' + d.x + ',' + d.y + ')';
        });

        text.attr('transform', function (d) {
          return 'translate(' + d.x + ',' + d.y + ')';
        });
      }

      circle.call(
        d3
          .drag()
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended)
      );

      // Reheat the simulation when drag starts, and fix the subject position.
      function dragstarted(event) {
        if (!event.active) force.alphaTarget(0.3).restart();
        event.subject.fx = event.subject.x;
        event.subject.fy = event.subject.y;
      }

      // Update the subject (dragged node) position during drag.
      function dragged(event) {
        event.subject.fx = event.x;
        event.subject.fy = event.y;
      }

      // Restore the target alpha so the simulation cools after dragging ends.
      // Unfix the subject position now that it’s no longer being dragged.
      function dragended(event) {
        if (!event.active) force.alphaTarget(0);
        event.subject.fx = null;
        event.subject.fy = null;
      }

      // sort the links by source, then target
      function sortLinks() {
        data.links.sort(function (a, b) {
          if (a.source > b.source) {
            return 1;
          } else if (a.source < b.source) {
            return -1;
          } else {
            if (a.target > b.target) {
              return 1;
            }
            if (a.target < b.target) {
              return -1;
            } else {
              return 0;
            }
          }
        });
      }

      //any links with duplicate source and target get an incremented 'linknum'
      function setLinkIndexAndNum() {
        for (var i = 0; i < data.links.length; i++) {
          if (
            i != 0 &&
            data.links[i].source == data.links[i - 1].source &&
            data.links[i].target == data.links[i - 1].target
          ) {
            data.links[i].linkindex = data.links[i - 1].linkindex + 1;
          } else {
            data.links[i].linkindex = 1;
          }
          // save the total number of links between two nodes
          if (
            mLinkNum[data.links[i].target + ',' + data.links[i].source] !==
            undefined
          ) {
            mLinkNum[data.links[i].target + ',' + data.links[i].source] =
              data.links[i].linkindex;
          } else {
            mLinkNum[data.links[i].source + ',' + data.links[i].target] =
              data.links[i].linkindex;
          }
        }
      }
    </script>
  </body>
</html>

© www.soinside.com 2019 - 2024. All rights reserved.