5.path动画描边

当你学了svg和d3的一些基础之后再来看这个案例,其中包含的知识点非常多,如果能全部掌握,相信你对d3的了解更上一层楼啦
当然你可以不学,直接复制粘贴,也是可以用的~

npm i d3 --save

<template>
  <div id="tree" :class="['tree-container']">
</template>
<script>
import * as d3 from "d3"
const curTranslate = {
  x: 0,
export default {
  data() {
    return {
      dataset: {
        cname: "主节点",
        children: [{
          cname: "子节点1",
          children: [{
            cname: "孙子点1"
            cname: "孙子点2"
          cname: "子节点2",
          cname: "子节点3",
      gsize: null,
  mounted() {
    this.initTree();
  methods: {
    initTree() {
      var timer;
      let treeContainer = document.getElementById("tree");
      var width = treeContainer.clientWidth;
      var height = treeContainer.clientHeight;
      var preTranslate = [0, 0];
      var svg = d3
        .select("#tree")
        .append("svg")
        .attr("width", width + "px")
        .attr("height", height + "px")
      var treeGroup = svg
        .append("g")
        .attr("transition", ".2s")
      let zoom = d3.zoom() // 拖拽与缩放
        .scaleExtent([.1, 10])
        .on("zoom", zoomed);
      svg.call(zoom);
      function zoomed(event) {
        let transofrm = d3.zoomTransform(this)
        treeGroup.attr("transform", transofrm.translate(curTranslate.x, curTranslate.y));
      var hierarchyData = d3.hierarchy(this.dataset).sum(function (d) {
        return d.value;
      var tree = d3
        .tree()
        .nodeSize([width / 6, height / 6.5])  // 以node为参考缩放,size ()以容器大小缩放,node节点过多会叠加
        .separation(function (a, b) { //定义邻居节点的距离
          return 1
      var treeData = tree(hierarchyData); //获取布 局数据
      var toolsWrap = svg.append("g").attr("id", 'toolsWrap').attr("style", "display: none;position:relative");
      toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "#000").on("click", function () {
        toolsWrap.attr("transform", `translate(0, 0)`).attr("style", "display: none;")
      toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "red").attr("x", 50)
      toolsWrap.append('rect').attr('width', 50).attr('height', 15).attr('fill', "green").attr("x", 100)
      var nodes = treeData.descendants(); // 节点数据
      var links = treeData.links(); //连线数据
      treeGroup.append("g")
        .selectAll("path") // 创建节点之间的连线
        .data(links)
        .enter()
        .append("path")
        .attr("d", function (d) {
          var start = { x: d.source.y, y: d.source.x };
          var end = { x: d.target.y, y: d.target.x };
          return `M${start.y},${start.x}L${start.y},${end.x - 30}L${end.y},${end.x - 30}L${end.y},${end.x}`
        .attr("fill", "none")
        .attr("stroke", "#999")
        .attr("stroke-width", 2)
        .attr("stroke-dasharray", function (d) {
          return this.getTotalLength()
        .attr("stroke-dashoffset", function (d) {
          return this.getTotalLength()
        .transition()
        .duration(1500)
        .attr("stroke-dashoffset", 0).delay(function (d) {
          return d.source.depth * 800
      var gs = treeGroup.append("g") //生成每个节点
        .selectAll("g")
        .data(nodes)
        .enter()
        .append("g")
        .attr("style", "cursor: pointer;")
        .attr("transform", function (d) {
          var cx = d.x;
          var cy = d.y;
          return `translate(${cx},${cy})`;
        .on("click", function (e) {
          let point = this.getBoundingClientRect(); // 被点击节点的dom信息
          let gsize = treeGroup.node().getBoundingClientRect(); // 整个group的信息
          preTranslate = treeGroup.attr('transform').match(/translate\(.*\)/g)[0].replace("translate(", "").replace(")", "").split(".");
          d3.zoomTransform(this).x = (gsize.x + width / 2 - (point.x + point.width / 2) + gsize.width / 2)
          d3.zoomTransform(this).y = (gsize.y + height / 2 - point.y)
          curTranslate.x = 0
          curTranslate.y = 0
          let curScale = d3.zoomTransform(this).k;
          treeGroup
            .transition()
            .attr('transform', `translate(${gsize.x + width / 2 - (point.x + point.width / 2) + gsize.width / 2}, ${gsize.y + height / 2 - point.y}) scale(${curScale})`) // 将被点击的节点居中显示
        .on("mouseenter", function (d, level) {
          if (timer) {
            clearTimeout(timer)
          let curScale = d3.zoomTransform(svg).k || 0;
          toolsWrap.attr("transform", `translate(0,0) scale(${curScale})`).attr("style", "display: block;")
          this.appendChild(toolsWrap.node())
          let bbox = this.getBBox(); // bbox 不会因为放大而宽高变化
          console.log(bbox);
          toolsWrap.attr("transform", `translate(${bbox.x}, ${bbox.height - 4}) scale(${curScale})`).attr("style", "display: block;")
        .on("mousemove", function (d, v) {
          // let point = this.getBoundingClientRect();
        }).on("mouseleave", function () {
          timer = setTimeout(() => {
            toolsWrap.attr("transform", `translate(0, 0)`).attr("style", "display: none;")
          }, 1000)
      gs.append("circle") // 创建节点的圆点
        .attr("r", 4)
        .attr("fill", "#bae637")
        .attr("stroke", "#000")
        .attr("stroke-width", 1)
      gs.append("text") // 创建节点对应的文本内容
        .attr("y", 20)
        .attr("dy", 10)
        .attr("fill", function (d) {
          return "orange"
        .attr("font-size", 18)
        .attr("style", (d) => {
          return !d.data.mainNode ? `transform : scale(${.8})` : ""
        .text(function (d) {
          return d.data.cname;
        }).attr("x", function (d) {
          let bbox = this.getBBox(); // 居中对齐
          return -bbox.width / 2
        }).each(function (d) {
          d3.select(this.parentNode)
            .insert("rect")
            .attr("width", this.getBBox().width + 20)
            .attr("height", this.getBBox().height + 5)
            .attr("fill", d => {
              return "#000"
            .attr("stroke", d => {
              return "#333"
            .attr("x", function (d) {
              let bbox = this.getBBox(); //居中对齐
              return -bbox.width / 2
            .attr("y", 9).each(function (d) {
              this.parentNode.insertBefore(this, this.parentNode.children[0])
            .attr("style", !d.data.mainNode ? `transform: scale(${.8})` : "")
      let gsize = treeGroup.node().getBoundingClientRect();
      curTranslate.x = width / 2;
      curTranslate.y = height / 2 - gsize.height / 2;
      treeGroup.attr("transform", `translate(${curTranslate.x}, ${curTranslate.y})`)
    update() {  // 重绘
      d3.selectAll("svg").remove()
      this.initTree();
</script>
<style scoped lang="scss" >
.tree-container {
  position: relative;
  width: 800px;
  height: 600px;
  user-select: none;
  border: 1px solid;
  background: linear- gradient(to top, transparent 12px, #d9d9d9 12px),
    linear-gradient(to left, transparent 12px, #d9d9d9 13px);
  background-size: 13px 13px;
</style>
最后编辑于:2022-07-02 04:18