美文网首页Vue
Vue D3 力导向图

Vue D3 力导向图

作者: 草帽lufei | 来源:发表于2018-12-09 19:54 被阅读271次

    1. 安装

    前端工程根目录下执行 yarn add d3 ,安装 d3 依赖包。安装的版本 "d3": "^5.7.0"

    2. vue 文件中引入 d3

    import * as d3 from 'd3'

    例如一个基础的 d3.vue 文件内容,包含基本的 <template> <script> <style>

    <template>
      <div>
        <svg width="960" height="600"></svg>
      </div>
    </template>
    <script>
    import * as d3 from 'd3'
    export default {
      data () {
        return {
        }
      }
    }
    </script>
    <style scoped></style>
    

    3.设置一个力导向图的基本骨架,添加了控制节点和线条的 css

    注意: .link line .node circle 节点和线条的样式不能写在 <style scoped></style> 中,因为 d3 数据是动态渲染的,scoped 中的样式无法控制动态生成的 dom

    <script>
    import * as d3 from 'd3'
    export default {
      data () {
        return {
        }
      },
      mounted () {
        let svg = d3.select('svg')
        let width = +svg.attr('width')
        let height = +svg.attr('height')
      },
      methods: {
       
      }
    }
    </script>
    <style scoped>
    svg {
      border: 1px solid #ccc;
    }
    </style>
    <style>
    .links line {
      stroke: #999;
      stroke-opacity: 0.6;
    }
    .nodes circle {
      stroke: #fff;
      stroke-width: 1.5px;
    }
    </style>
    

    添加节点数据

        let nodesData = [
          { 'name': 'Lillian', 'sex': 'F' },
          { 'name': 'Gordon', 'sex': 'M' },
          { 'name': 'Sylvester', 'sex': 'M' },
          { 'name': 'Mary', 'sex': 'F' },
          { 'name': 'Helen', 'sex': 'F' },
          { 'name': 'Jamie', 'sex': 'M' },
          { 'name': 'Jessie', 'sex': 'F' },
          { 'name': 'Ashton', 'sex': 'M' },
          { 'name': 'Duncan', 'sex': 'M' },
          { 'name': 'Evette', 'sex': 'F' },
          { 'name': 'Mauer', 'sex': 'M' },
          { 'name': 'Fray', 'sex': 'F' },
          { 'name': 'Duke', 'sex': 'M' },
          { 'name': 'Baron', 'sex': 'M' },
          { 'name': 'Infante', 'sex': 'M' },
          { 'name': 'Percy', 'sex': 'M' },
          { 'name': 'Cynthia', 'sex': 'F' }
        ]
    

    使用节点数据设置模拟器

       let simulation = d3.forceSimulation().nodes(nodesData)
    

    添加定心力和充电力

        simulation
          .force('charge_force', d3.forceManyBody())
          .force('center_force', d3.forceCenter(width / 2, height / 2))
    

    在svg元素中绘制圆圈

        let node = svg.append('g')
          .attr('class', 'nodes')
          .selectAll('circle')
          .data(nodesData)
          .enter()
          .append('circle')
          .attr('r', 10)
          .attr('fill', this.circleColor)
    

    methods 中添加 circleColor 函数

        circleColor (d) {
          if (d.sex === 'M') {
            return 'blue'
          } else {
            return 'pink'
          }
        },
    

    每次作出举动时需要更新节点位置

        simulation.on('tick', tickAction)
    
        function tickAction () {
          node
            .attr('cx', (d) => { return d.x })
            .attr('cy', (d) => { return d.y })
        }
    

    现在图上已经有一些圆圈了,如下效果


    添加连线,指定链接数据

        let linksData = [
          { 'source': 'Sylvester', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Lillian', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Mary', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Jamie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Jessie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Helen', 'type': 'A' },
          { 'source': 'Helen', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Mary', 'target': 'Lillian', 'type': 'A' },
          { 'source': 'Ashton', 'target': 'Mary', 'type': 'A' },
          { 'source': 'Duncan', 'target': 'Jamie', 'type': 'A' },
          { 'source': 'Gordon', 'target': 'Jessie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Fray', 'type': 'E' },
          { 'source': 'Fray', 'target': 'Mauer', 'type': 'A' },
          { 'source': 'Fray', 'target': 'Cynthia', 'type': 'A' },
          { 'source': 'Fray', 'target': 'Percy', 'type': 'A' },
          { 'source': 'Percy', 'target': 'Cynthia', 'type': 'A' },
          { 'source': 'Infante', 'target': 'Duke', 'type': 'A' },
          { 'source': 'Duke', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Duke', 'target': 'Sylvester', 'type': 'A' },
          { 'source': 'Baron', 'target': 'Duke', 'type': 'A' },
          { 'source': 'Baron', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Evette', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Cynthia', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Cynthia', 'target': 'Jamie', 'type': 'E' },
          { 'source': 'Mauer', 'target': 'Jessie', 'type': 'E' }
        ]
    

    创建链接力

        let linkForce = d3.forceLink(linksData)
          .id((d) => { return d.name })
    

    把链接力添加到模拟器中

      simulation.force('links', linkForce)
    

    在页面绘制链接

        let link = svg.append('g')
          .attr('class', 'links')
          .selectAll('line')
          .data(linksData)
          .enter()
          .append('line')
          .attr('stroke-width', 2)
          .style('stroke', this.linkColor)
    

    methods 中添加 linkColor 函数

        linkColor (d) {
          if (d.type === 'A') {
            return 'green'
          } else {
            return 'red'
          }
        }
    

    在 tickAction 函数中更新链接位置

          link
            .attr('x1', (d) => { return d.source.x })
            .attr('y1', (d) => { return d.source.y })
            .attr('x2', (d) => { return d.target.x })
            .attr('y2', (d) => { return d.target.y })
    

    目前就实现了一个简单的力导向图


    d3.vue 完整代码如下

    <template>
      <div>
        <h1>Knowledge Graph</h1>
        <svg width="960" height="600"></svg>
      </div>
    </template>
    <script>
    import * as d3 from 'd3'
    export default {
      data () {
        return {
        }
      },
      mounted () {
        let svg = d3.select('svg')
        let width = +svg.attr('width')
        let height = +svg.attr('height')
    
        let nodesData = [
          { 'name': 'Lillian', 'sex': 'F' },
          { 'name': 'Gordon', 'sex': 'M' },
          { 'name': 'Sylvester', 'sex': 'M' },
          { 'name': 'Mary', 'sex': 'F' },
          { 'name': 'Helen', 'sex': 'F' },
          { 'name': 'Jamie', 'sex': 'M' },
          { 'name': 'Jessie', 'sex': 'F' },
          { 'name': 'Ashton', 'sex': 'M' },
          { 'name': 'Duncan', 'sex': 'M' },
          { 'name': 'Evette', 'sex': 'F' },
          { 'name': 'Mauer', 'sex': 'M' },
          { 'name': 'Fray', 'sex': 'F' },
          { 'name': 'Duke', 'sex': 'M' },
          { 'name': 'Baron', 'sex': 'M' },
          { 'name': 'Infante', 'sex': 'M' },
          { 'name': 'Percy', 'sex': 'M' },
          { 'name': 'Cynthia', 'sex': 'F' }
        ]
    
        let linksData = [
          { 'source': 'Sylvester', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Lillian', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Mary', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Jamie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Jessie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Helen', 'type': 'A' },
          { 'source': 'Helen', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Mary', 'target': 'Lillian', 'type': 'A' },
          { 'source': 'Ashton', 'target': 'Mary', 'type': 'A' },
          { 'source': 'Duncan', 'target': 'Jamie', 'type': 'A' },
          { 'source': 'Gordon', 'target': 'Jessie', 'type': 'A' },
          { 'source': 'Sylvester', 'target': 'Fray', 'type': 'E' },
          { 'source': 'Fray', 'target': 'Mauer', 'type': 'A' },
          { 'source': 'Fray', 'target': 'Cynthia', 'type': 'A' },
          { 'source': 'Fray', 'target': 'Percy', 'type': 'A' },
          { 'source': 'Percy', 'target': 'Cynthia', 'type': 'A' },
          { 'source': 'Infante', 'target': 'Duke', 'type': 'A' },
          { 'source': 'Duke', 'target': 'Gordon', 'type': 'A' },
          { 'source': 'Duke', 'target': 'Sylvester', 'type': 'A' },
          { 'source': 'Baron', 'target': 'Duke', 'type': 'A' },
          { 'source': 'Baron', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Evette', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Cynthia', 'target': 'Sylvester', 'type': 'E' },
          { 'source': 'Cynthia', 'target': 'Jamie', 'type': 'E' },
          { 'source': 'Mauer', 'target': 'Jessie', 'type': 'E' }
        ]
    
        let simulation = d3.forceSimulation()
          .nodes(nodesData)
    
        simulation
          .force('charge_force', d3.forceManyBody())
          .force('center_force', d3.forceCenter(width / 2, height / 2))
    
        let node = svg.append('g')
          .attr('class', 'nodes')
          .selectAll('circle')
          .data(nodesData)
          .enter()
          .append('circle')
          .attr('r', 10)
          .attr('fill', this.circleColor)
    
        simulation.on('tick', tickAction)
    
        function tickAction () {
          node
            .attr('cx', (d) => { return d.x })
            .attr('cy', (d) => { return d.y })
    
          link
            .attr('x1', (d) => { return d.source.x })
            .attr('y1', (d) => { return d.source.y })
            .attr('x2', (d) => { return d.target.x })
            .attr('y2', (d) => { return d.target.y })
        }
    
       let linkForce = d3.forceLink(linksData)
          .id((d) => { return d.name })
    
        simulation.force('links', linkForce)
    
        let link = svg.append('g')
          .attr('class', 'links')
          .selectAll('line')
          .data(linksData)
          .enter()
          .append('line')
          .attr('stroke-width', 2)
          .style('stroke', this.linkColor)
      },
      methods: {
        circleColor (d) {
          if (d.sex === 'M') {
            return 'blue'
          } else {
            return 'pink'
          }
        },
        linkColor (d) {
          if (d.type === 'A') {
            return 'green'
          } else {
            return 'red'
          }
        }
      }
    }
    </script>
    <style scoped>
    svg {
      border: 1px solid #ccc;
    }
    </style>
    <style>
    .links line {
      stroke: #999;
      stroke-opacity: 0.6;
    }
    .nodes circle {
      stroke: #fff;
      stroke-width: 1.5px;
    }
    </style>
    
    

    GitHub

    https://github.com/gywgithub/vue-d3-examples

    相关文章

      网友评论

        本文标题:Vue D3 力导向图

        本文链接:https://www.haomeiwen.com/subject/nolfxqtx.html