使用D3绘制粘性力布局图

379 阅读1分钟

在使用d3绘制力布局图前,必须先了解一种基本的数据结构。

数据结构:图(Graph)

图一般由顶点和边(连线)构成,在代码中,我们一般使用两个数组来记录。

const nodes = ['a', 'b', 'c'];
// nodes表示三个结点
const links = [ { source: 'a', target: 'b' }, { source: 'a', target: 'c' } ]
// links表示有两条线,ab和ac

为了使每次渲染都绘制出不同的图形,连线的起始点选择使用随机数。

const graph = {
  nodes: Array.from({length:100}, () => ({})),
  links: []
}
graph.nodes.forEach((item, index) => {
  graph.links.push({
    source: Number.parseInt(Math.random() * 100),
    target: index
  })
})
const size = getWindowSize()
const width = size.width
const height = size.height
const svg = d3.select("#svg-wrap-force").append("svg").attr("viewBox", [0, 0, width, height]),
  g = svg.append("g"),
  link = g
    .selectAll(".link")
    .data(graph.links)
    .join("line")
    .classed("link", true),
  node = g
    .selectAll(".node")
    .data(graph.nodes)
    .join("circle")
    .attr("r", 5)
    .classed("node", true)
    .classed("fixed", d => d.fx !== undefined)

svg.call(d3.zoom()
  .extent([[0, 0], [width, height]])
  .scaleExtent([0.1, 8])
  .on("zoom", zoomed))

function zoomed({transform}) {
  g.attr("transform", transform)
}

const simulation = d3
  .forceSimulation()
  .nodes(graph.nodes)
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2))
  .force("link", d3.forceLink(graph.links))
  .on("tick", tick)

const drag = d3
  .drag()
  .on("start", dragstart)
  .on("drag", dragged)

node.call(drag).on("click", click)

function getWindowSize () {
  return {
    height: window.innerHeight,
    width: window.innerWidth
  }
}

function tick() {
  link
    .attr("x1", d => d.source.x)
    .attr("y1", d => d.source.y)
    .attr("x2", d => d.target.x)
    .attr("y2", d => d.target.y)
  node
    .attr("cx", d => d.x)
    .attr("cy", d => d.y)
}

function click(event, d) {
  delete d.fx;
  delete d.fy;
  d3.select(this).classed("fixed", false)
  simulation.alpha(1).restart()
}

function dragstart() {
  d3.select(this).classed("fixed", true)
}

function clamp(x, lo, hi) {
  return x < lo ? lo : x > hi ? hi : x;
}

function dragged(event, d) {
  d.fx = clamp(event.x, 0, width)
  d.fy = clamp(event.y, 0, height)
  simulation.alpha(1).restart()
}

最终绘制效果

image.png

Demo地址