d3.js+vue生成动力图

本文主要实现了动力图中:

        节点的显示;

        节点与节点之间关系的连接,以及对应的关系类型的显示;

        节点的拖拽;

        图谱的缩放

1.先上效果:

2. 以下是完整的代码部分:

<template>
  <div ref="chart" className="ggraph"></div>
</template>

<script>
import * as d3 from 'd3';

export default {
  data() {
    return {
      nodes: [
        {id: 1, name: '刘备', type: '皇上'},
        {id: 2, name: '关羽', type: '将军'},
        {id: 3, name: '张飞', type: '将军'},
        {id: 4, name: '诸葛亮', type: '丞相'},
        {id: 5, name: '小兵1', type: '士兵'},
        {id: 6, name: '小兵2', type: '士兵'},
      ],
      links: [
        {source: 1, target: 2, relate: '将军'},
        {source: 1, target: 3, relate: '将军'},
        {source: 1, target: 4, relate: '丞相'},
        {source: 2, target: 5, relate: '下属'},
        {source: 2, target: 6, relate: '下属'},
        {source: 3, target: 5, relate: '下属'},
      ]
    };
  },
  mounted() {
    this.drawChart();
  },
  methods: {
    drawChart() {
      const data = {
        nodes: this.nodes,
        links: this.links
      };

      const width = 600;
      const height = 400;

      // 创建SVG容器
      const svg = d3.select(this.$refs.chart)
        .append('svg')
        .attr('width', width)
        .attr('height', height);

      const g = svg.append('g'); // 将 g 元素放在 svg 元素内部

      // 创建力导向图模拟器
      const simulation = d3.forceSimulation(data.nodes)
        .force('link', d3.forceLink(data.links).id(d => d.id).distance(100))
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter(width / 2, height / 2));
      // 创建边
      const link = g.selectAll('line')
        .data(data.links)
        .enter()
        .append('line')
        .attr('stroke', '#999')
        .attr('stroke-width', 2)
      //创建节点
      const node = g.selectAll('.node')
        .data(data.nodes)
        .enter()
        .append('g')
        .attr('class', 'node')
        .style('fill', 'black');
      node.append('circle')
        .attr('r', 18)
        .attr('fill', 'steelblue')
        .style('fill', 'blue')
        .call(d3.drag() //节点拖拽
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended));
      //节点上面显示文字
      node.append('text')
        .text(d => d.name)
        .attr('text-anchor', 'middle')
        .attr('dy', 4)
        .attr('font-size', d => Math.min(2 * d.radius, 20))
        .attr('fill', 'black')
        .style('pointer-events', 'none');
      // 连线文字
      const linkText = g.selectAll('.linktext')
        .data(data.links)
        .enter()
        .append('text')
        .attr('class', 'linktext')
        .style('fill', 'black')
        .style('font-size', 8)
        .style('text-anchor', 'middle')
        .text(d => d.relate)
        .style('pointer-events', 'none');

      // 缩放
      svg.call(d3.zoom().on("zoom", function (event) {
        g.attr("transform", event.transform);
      }));
      // 监听力导向图模拟器的tick事件,更新节点和边的位置
      simulation
        .on('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('transform', d => `translate(${d.x},${d.y})`);
          // 更新连线文字的位置
          linkText
            .attr('x', d => (d.source.x + d.target.x) / 2)
            .attr('y', d => (d.source.y + d.target.y) / 2);
        });

      function dragstarted(event, d) {
        if (!event.active) simulation.alphaTarget(0.5).restart();
        d.fx = d.x;
        d.fy = d.y;
      }

      function dragged(event, d) {
        d.fx = event.x;
        d.fy = event.y;
      }

      function dragended(event, d) {
        if (!event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
    }
  }
};
</script>

<style>
svg {
  background-color: #d1e9ff;
}
</style>

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值