用react+d3 v6画力引导图

133 阅读2分钟
let width = 800;
let height = 800;
let svg = d3
  .select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

let marge = { top: 10, bottom: 10, left: 10, right: 10 };

let g = svg
  .append("g")
  .attr("transform", "translate(" + marge.top + "," + marge.left + ")");

//准备数据
let nodes = [
  //节点集
  { name: "1", group: 2.5 },
  { name: "2", group: 2 },
  { name: "3", group: 2 },
  { name: "4", group: 2 },
  { name: "5", group: 2 },
  { name: "6", group: 2 },
  { name: "7", group: 2 },
  { name: "8", group: 2 },
  { name: "9", group: 2 },
];
let edges = [
  //边集
  { source: 0, target: 1, value: 2,relation:"连接线" }, //value控制线的长短
  { source: 0, target: 0, value: 2,relation:""  }, //value控制线的长短
  { source: 0, target: 2, value: 2,relation:"连接线"  },
  { source: 0, target: 3, value: 2,relation:"连接线"  },
  { source: 0, target: 4, value: 2,relation:"连接线"  },
  { source: 0, target: 5, value: 2,relation:"连接线"  },
  { source: 0, target: 6, value: 2,relation:"连接线"  },
  { source: 0, target: 7, value: 2,relation:"连接线"  },
  { source: 0, target: 8, value: 2,relation:"连接线"  },
  { source: 8, target: 0, value: 2,relation:"连接线"  },
  { source: 8, target: 8, value: 2,relation:""  },
  { source: 8, target: 6, value: 2,relation:"连接线"  },
];

let forceSimulation = d3
  .forceSimulation()
  .force("link", d3.forceLink())
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter());
//生成节点数据
forceSimulation.nodes(nodes).on("tick", ticked); 
//生成边数据
forceSimulation
  .force("link")
  .links(edges)
  .distance(function (d) {
    //每一边的长度
    return d.value * 100;
  });
//设置图形的中心位置
forceSimulation
  .force("center")
  .x(width / 2)
  .y(height / 2);
//在浏览器的控制台输出
console.log(nodes);
console.log(edges[0].source);

let defs = svg.append("defs");
let arrow_path = "M0,0  L5.19,3 L0,6 L0,0";
defs
  .append("marker")
  .attr("id", "arrow0")
  .attr("markerUnits", "userSpaceOnUse")
  .attr("markerWidth", "12")
  .attr("markerHeight", "12")
  .attr("viewBox", "0 0 12 12")
  .attr("refX", "20")
  .attr("refY", "3")
  .attr("orient", "auto")
  .append("path")
  .attr("d", arrow_path)
  .attr("fill", "#757f7f");

  defs
  .append("marker")
  .attr("id", "arrow1")
  .attr("markerUnits", "userSpaceOnUse")
  .attr("markerWidth", "12")
  .attr("markerHeight", "12")
  .attr("viewBox", "0 0 12 12")
  .attr("refX", "5")
  .attr("refY", "2.5")
  .attr("orient", "auto")
  .append("path")
  .attr("d", arrow_path)
  .attr("fill", "#757f7f");
//绘制边
let links = g
  .append("g")
  .selectAll("path")
  .data(edges)
  .enter()
  .append("path")
  .attr("stroke", function (d, i) {
    return "#757f7f";
  })
  .attr("stroke-width", 1)
  .attr("marker-end",(d)=>{
    let x1 = d.source.x;
    let y1 = d.source.y;
    let x2 = d.target.x;
    let y2 = d.target.y;
    if (x1 === x2 && y1 === y2) {
      return "url(#arrow1)"
    }
    return "url(#arrow0)"})
  .attr("fill", "#d2691e00");
//边上文字
let linksText = g
  .append("g").attr("font-size",6)
  .selectAll("text")
  .data(edges)
  .enter()
  .append("text")
  .text(function (d) {
    return d.relation;
  });
//建立用来放在每个节点和对应文字的分组<g>
let gs = g
  .selectAll(".circleText")
  .data(nodes)
  .enter()
  .append("g")
  .attr("transform", function (d, i) {
    let cirX = d.x;
    let cirY = d.y;
    return "translate(" + cirX + "," + cirY + ")";
  })
  .call(
    d3
      .drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended)
  );

//绘制节点
gs.append("circle")
  // .attr("r",20)
  .attr("r", function (d, i) {
    //圆圈半径
    return 15;
  })
  .attr("fill", function (d, i) {
    //return colorScale(i);
    return "#f1bcd8";
  });
//文字
gs.append("text").attr("font-size",10)
  /*.attr("x",-10)
    .attr("y",-20)
    .attr("dy",10)*/
  .attr("x", -25)
  .attr("y", -5)
  .attr("dy", 10)
  .text(function (d) {
    return d.name;
  });
function ticked() {
  links.attr("d", (d) => {
    let x1 = d.source.x;
    let y1 = d.source.y;
    let x2 = d.target.x;
    let y2 = d.target.y;
    if (x1 === x2 && y1 === y2) {
      return `M${x1 - 15},${y1} A${25},${25} ${0},${1},${1} ${
        x1 + 15
      },${y1}`;
    }
   const {x:moveX,y:moveY}= getTargetXY(15,d.value*100,d.source,d.target);
    return `M${x1} ${y1}L${x2} ${y2}`;
  });

  linksText
    .attr("x", function (d) {
      return (d.source.x + d.target.x) / 2;
    })
    .attr("y", function (d) {
      return (d.source.y + d.target.y) / 2;
    });

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

function dragstarted(event) {
  if (!event.active) forceSimulation.alphaTarget(0.3).restart();
  event.subject.fx = event.subject.x;
  event.subject.fy = event.subject.y;
}

function dragged(event) {
  event.subject.fx += event.dx;
  event.subject.fy += event.dy;
}

function dragended(event) {
  if (!event.active) forceSimulation.alphaTarget(0);
  event.subject.fx = null;
  event.subject.fy = null;
}