D3 v4.x学习(5)—— 力导向图

  本章主要分享可以拖拽的力导向图代码。

  力导向图(图左上角)一般可以用于关系展现。本文参考的是乡村爱情中的部分人物关系图。

  目前研究成果

  (希望给大家在探索D3 V4.x版本的API的道路省点时间,有时候是真的想给那个作者寄刀片

 其他图的代码会在后面的文章中展示。

 差不多已经画了十来个图,总结一下D3的作用。

 要总结D3的作用,就要先从绘制一张图的流程说起。D3的绘制基本是有套路的。

 比如就今天的demo来说,你要绘制一张力导向图

 1.首先你需要有数据源(包括人物,人物关系)

 2. 然后你需要动态计算人物的节点位置(node position) 以及 人物的关系连线(line 的起点和终点)

 3.有了点和线,你就可以通过svg标签进行绘制了。

 4.最后如果你想要图美观一些的话可以加点样式和动画,如果要求再高一些,可以加一些dom事件。

 以上四个步骤中,你唯一不会的是哪步?没错就是第二步,如何计算的问题!

 D3的作用就是帮你总结了一些算法,他可以帮你完成很多点和线的复杂计算,最终你通过计算得到的点和线绘制图形

 话不多说,上力导向图的代码。

<template>
  <div id='svgContainer' style="">
    <div class="every">
      <h3>力导向图探究</h3>
      <div class="svg" id="forceDirected"></div>
    </div>
  </div>
</template>
<script>
import * as d3 from 'd3'
export default {
  data () {
    return {
      pieData: [{
        name: '小米',
        value: 60.8
      }, {
        name: '华为',
        value: 20.8
      }, {
        name: '联想',
        value: 30.4
      }, {
        name: '三星',
        value: 40.8
      }, {
        name: '苹果',
        value: 90.8
      }, {
        name: '其他',
        value: 100.8
      } ]
    }
  },
  methods: {
    forceDirected () {
      let width = 400
      let height = 400
      let padding = {
        left: 30,
        right: 30,
        top: 20,
        bottom: 20
      }
      // 人物节点 name表示名称
      let nodes = [
        {name: '谢大脚'},
        {name: '王长贵'},
        {name: '王大拿'},
        {name: '谢小梅'},
        {name: '谢广坤'},
        {name: '刘能'},
        {name: '赵四'},
        {name: '刘大脑袋'},
        {name: '赵玉田'},
        {name: '刘英'},
        {name: '王老七'},
        {name: '王小蒙'},
        {name: '谢永强'}
      ]
      // 人物关系,source和target表示连线两端的节点,节点的序号从0开始,relation是自己随便加的
      let links = [
        {source: 0, target: 1, relation: '两口子'},
        {source: 0, target: 2, relation: '曾爱慕'},
        {source: 0, target: 3, relation: '亲戚'},
        {source: 0, target: 4, relation: '曾爱慕'},
        {source: 2, target: 7, relation: '上下属'},
        {source: 4, target: 5, relation: '死对头'},
        {source: 4, target: 10, relation: '亲家'},
        {source: 5, target: 9, relation: '父女'},
        {source: 5, target: 6, relation: '亲家'},
        {source: 10, target: 11, relation: '妇女'},
        {source: 11, target: 12, relation: '两口子'},
        {source: 4, target: 12, relation: '父子'},
        {source: 8, target: 9, relation: '两口子'}
      ]
      let svg = d3.select('#forceDirected')
        .append('svg')
        .attr('width', width)
        .attr('height', height)

      // 通过布局来转换数据,然后进行绘制
      let simulation = d3.forceSimulation()
        .nodes(nodes)
        .force('link', d3.forceLink(links).distance(200))
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter((width - padding.left - padding.right) / 2, (height - padding.top - padding.bottom) / 2))
      let color = d3.scaleOrdinal(d3.schemeCategory20)

      // 添加节点
      svg.selectAll('circle')
        .data(nodes)
        .enter()
        .append('circle')
        .attr('r', 10)
        .style('fill', function (d, i) {
          return color(i)
        })
        // 添加圆圈的拖拽事件,同时他会触发simulation的tick事件,重新布局该区域
        .call(d3.drag()
          .on('start', function (d) {
            if (!d3.event.active) {
              simulation.alphaTarget(0.8).restart() // 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1]
            }
            d.fx = d.x
            d.fy = d.y
          })
          .on('drag', function (d) {
            d.fx = d3.event.x
            d.fy = d3.event.y
          })
          .on('end', function (d) {
            if (!d3.event.active) {
              simulation.alphaTarget(0)
            }
            d.fx = null
            d.fy = null
          })
        )
      // 添加描述
      svg.selectAll('text')
        .data(nodes)
        .enter()
        .append('text')
        .style('font-size', '12px')
        .style('fill', '#000')
        .attr('dx', 0)
        .attr('dy', 0)
        .text(function (d) { return d.name })
      // 添加relation
      svg.selectAll('.relation')
        .data(links)
        .enter()
        .append('text')
        .style('fill', 'red')
        .style('font-size', '11px')
        .attr('class', 'relation')
        .attr('dx', 0)
        .attr('dy', 0)
        .text(function (d) { return d.relation })
      // 添加连线
      svg.selectAll('line')
        .data(links)
        .enter()
        .append('line')
        .style('stroke', '#ccc')
        .style('stroke-width', 2)
     //数据重绘
      simulation.on('tick', function () {
        svg.selectAll('circle')
          .attr('cx', function (d) { return d.x })
          .attr('cy', function (d) { return d.y })
        svg.selectAll('text')
          .attr('x', function (d) { return d.x })
          .attr('y', function (d) { return d.y })
        svg.selectAll('line')
          .attr('x1', function (d) { return d.source.x })
          .attr('y1', function (d) { return d.source.y })
          .attr('x2', function (d) { return d.target.x })
          .attr('y2', function (d) { return d.target.y })
        svg.selectAll('.relation')
          .attr('x', function (d) { return (d.source.x + d.target.x) / 2 })
          .attr('y', function (d) { return (d.source.y + d.target.y) / 2 })
      })
    }
  },
  mounted () {
    this.forceDirected ()
  }
}
</script>
<style lang="less">
#svgContainer{
  width: 100%;
  height: 100%;
  .every{
    width: 400px;
    height: 425px;
    margin:15px;
    float: left;
    h3{
      margin:0;
      .button{
        float: right;
        margin-right: 20px;
        font-size: 14px;
        cursor: pointer;
        padding: 2px 8px;
        border:1px solid #ccc;
        background: yellowgreen;
        border-radius: 4px;
        &:hover{
          background: violet;
        }
      }
    }
    .svg{
      width: 400px;
      height: 400px;
    }
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/dkr380205984/article/details/81390739