04、声明式绘图:利用SVG绘制层次关系图

利用SVG绘制层次关系图

一、SVG简介

SVG 的全称是 Scalable Vector Graphics,可缩放矢量图,它是浏览器支持的一种基于 XML 语法的图像格式。

SVG 可以作为 HTML 内嵌元素使用,也可以作为图像通过 img 元素加载,或者绘制到 Canvas 内。

二、SVG基本绘制

svg元素是SVG的根元素,属性xmlns是xml的命名空间。命名空间是用于在XML文档中避免元素名称冲突的机制,在SVG中,xmlns属性通常是指定为 http://www.w3.org/2000/svg,表示SVG文档的命名空间为SVG规范定义的标准命名空间。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>如何用SVG图形元素绘制可视化图表</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg"  width="150" height="150">
            <circle
                cx="100"
                cy="50"
                r="40"
                stroke="black"
                stroke-width="2"
                fill="orange"
            />
        </svg>
    </body>
</html>

在这里插入图片描述

svg可以通过属性viewBox设置可视区域,例如以下定义了一个视口大小为150*150的svg,并且可视区域为(0,0)到(100,50)的范围。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>如何用SVG图形元素绘制可视化图表</title>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg"  width="150" height="150" viewBox="0 0 100 50">
            <circle
                cx="100"
                cy="50"
                r="40"
                stroke="black"
                stroke-width="2"
                fill="orange"
            />
        </svg>
    </body>
</html>

在这里插入图片描述

三、SVG绘制层次关系图

SVG 元素要使用 document.createElementNS 方法来创建。该方法用于创建一个具有指定的命名空间 URI 和限定名称的元素。
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>如何用SVG图形元素绘制可视化图表</title>
  </head>
  <body>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      version="1.1"
      width="800"
      height="800"
      viewBox="0 0 1600 1600"
    ></svg>
    <script src="https://d3js.org/d3-hierarchy.v1.min.js"></script>
  </body>
  <script type="module">
   import dataSource from "./data.js";
    // 创建一个树状结构,并计算每个节点的值
    const regions = d3
      .hierarchy(dataSource)
      .sum((d) => 1)
      .sort((a, b) => b.value - a.value);
    // 设置树状结构的大小,并设置节点之间的间距
    const pack = d3.pack().size([1600, 1600]).padding(3);
    // 将树状结构转换为svg元素
    const root = pack(regions);
    const svgRoot = document.querySelector("svg");
    // 绘制svg元素
    const draw = (
      parent,
      node,
      { fillStyle = "rgba(0, 0, 0, 0.2)", textColor = "white" } = {}
    ) => {
      const children = node.children;
      const { x, y, r } = node;
      const circle = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "circle"
      );
      circle.setAttribute("cx", x);
      circle.setAttribute("cy", y);
      circle.setAttribute("r", r);
      circle.setAttribute("fill", fillStyle);
      parent.appendChild(circle);
      if (children) {
        const group = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "g"
        );
        group.setAttribute("data-name", node.data.name);
        for (let i = 0; i < children.length; i++) {
          draw(group, children[i], { fillStyle, textColor });
        }
        parent.appendChild(group);
      } else {
        const text = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "text"
        );
        text.setAttribute("x", x);
        text.setAttribute("y", y);
        text.setAttribute("fill", textColor);
        text.setAttribute("text-anchor", "middle");
        text.setAttribute("font-size", "14");
        text.setAttribute("font-family", "Arial");
        text.textContent = node.data.name;
        parent.appendChild(text);
      }
    };
    draw(svgRoot, root);
  </script>
</html>

四、SVG层级关系图添加交互

svg元素类同于HTML可以被DOM API操作,通过事件冒泡处理每个圆上面的鼠标事件。
在这里插入图片描述

完整代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>如何用SVG图形元素绘制可视化图表</title>
    <style>
      h1#title {
        position: absolute;
        width: 800px;
        text-align: center;
      }
    </style>
  </head>
  <body>
    <h1 id="title"></h1>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      version="1.1"
      width="800"
      height="800"
      viewBox="0 0 1600 1600"
    ></svg>
    <script src="https://d3js.org/d3-hierarchy.v1.min.js"></script>
    <script type="module">
      import dataSource from "./data.js";
      // 创建一个树状结构,并计算每个节点的值
      const regions = d3
        .hierarchy(dataSource)
        .sum((d) => 1)
        .sort((a, b) => b.value - a.value);
      // 设置树状结构的大小,并设置节点之间的间距
      const pack = d3.pack().size([1600, 1600]).padding(3);
      // 将树状结构转换为svg元素
      const root = pack(regions);
      const svgroot = document.querySelector("svg");
      // 绘制svg元素
      const draw = (
        parent,
        node,
        { fillStyle = "rgba(0, 0, 0, 0.2)", textColor = "white" } = {}
      ) => {
        const children = node.children;
        const { x, y, r } = node;
        const circle = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "circle"
        );
        circle.setAttribute("cx", x);
        circle.setAttribute("cy", y);
        circle.setAttribute("r", r);
        circle.setAttribute("fill", fillStyle);
        circle.setAttribute("data-name", node.data.name);
        parent.appendChild(circle);
        if (children) {
          const group = document.createElementNS(
            "http://www.w3.org/2000/svg",
            "g"
          );
          group.setAttribute("data-name", node.data.name);
          for (let i = 0; i < children.length; i++) {
            draw(group, children[i], { fillStyle, textColor });
          }
          parent.appendChild(group);
        } else {
          // 绘制文本
          const text = document.createElementNS(
            "http://www.w3.org/2000/svg",
            "text"
          );
          text.setAttribute("fill", textColor);
          text.setAttribute("font-family", "Arial");
          text.setAttribute("font-size", "1.5rem");
          text.setAttribute("text-anchor", "middle");
          text.setAttribute("x", x);
          text.setAttribute("y", y);
          text.textContent = node.data.name;
          parent.appendChild(text);
        }
      };
      draw(svgroot, root);
      const titleEl = document.getElementById("title");
      // 获取文本
      function getTitle(target) {
        const name = target.getAttribute("data-name");
        if (target.parentNode && target.parentNode.nodeName === "g") {
          const parentName = target.parentNode.getAttribute("data-name");
          return `${parentName}-${name}`;
        }
        return name;
      }

      // 给svg添加mousemove事件
      let activeTarget = null;
      svgroot.addEventListener("mousemove", (evt) => {
        let target = evt.target;
        // 如果是文本节点
        if (target.nodeName === "text") {
          // previousSibling 返回当前节点的前一个兄弟节点,没有则返回null.
          // 因为这里的text前一个节点就是circle
          target = target.previousSibling;
        }
        // 判断是否是已经激活的
        if (activeTarget !== target) {
          if (activeTarget) {
            activeTarget.setAttribute("fill", "rgba(0, 0, 0, 0.2)");
          }
        }
        target.setAttribute("fill", "rgba(0, 128, 0, 0.1)");
        activeTarget = target;
        titleEl.textContent = getTitle(target);
      });
    </script>
  </body>
</html>
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值