【图形基础篇】02 # 指令式绘图系统:如何用Canvas绘制层次关系图?

简介: 【图形基础篇】02 # 指令式绘图系统:如何用Canvas绘制层次关系图?

说明

【跟月影学可视化】学习笔记。



如何用 Canvas 绘制几何图形?


1. Canvas 元素和 2D 上下文


Canvas 元素上的 width 和 height 属性不等同于 Canvas 元素的 CSS 样式的属性。这样分开能更方便地适配不同的显示设备。


   CSS 属性中的宽高影响 Canvas 在页面上呈现的大小

   HTML 属性中的宽高则决定了 Canvas 的坐标系

Canvas 的 HTML 属性宽高为画布宽高,CSS 样式宽高为样式宽高。

<!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>如何用Canvas绘制层次关系图</title>
    <style>
        canvas {
            width: 256px;
            height: 256px;
        }
    </style>
</head>
<body>
    <canvas width="512" height="512"></canvas>
</body>
</html>


f7598dad36304a929f704c5358b7926b.png


2. Canvas 的坐标系

默认左上角为坐标原点,x 轴水平向右,y 轴垂直向下。

e36e06c5af374619b2d120b7f9a013ba.png


3. 利用 Canvas 绘制几何图形

3.1、获取 Canvas 上下文

首先是获取 Canvas 元素。

const canvas = document.querySelector("canvas");


通过 getContext 方法拿到它的上下文对象。

const context = canvas.getContext("2d");


3.2、用 Canvas 上下文绘制图形

context 对象 API


  1. 设置状态的 API,可以设置或改变当前的绘图状态(颜色、线宽、坐标变换等)。
  2. 绘制指令 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>如何用Canvas绘制层次关系图</title>
    <style>
        canvas {
            border: 1px dashed salmon;
        }
    </style>
</head>
<body>
    <canvas width="512" height="512"></canvas>
    <script>
        const canvas = document.querySelector("canvas");
        const context = canvas.getContext("2d");
        /*****方法一:直接改变要绘制的图形顶点的坐标位置*****/
        // 将正方形填充成绿色
        context.fillStyle = "green";
        // 绘制的路径
        context.beginPath();
        // 调用 rect 指令完成绘图
        context.rect(canvas.width/2 - 50, canvas.height/2 - 50, 100, 100);
        // 将绘制的内容真正输出到画布中
        context.fill();
        /*****方法二:对 Canvas 画布的整体做一个平移操作*****/
        // 将正方形填充成橙红色
        context.fillStyle = "salmon";
        // 绘制的路径
        context.beginPath();
        // 暂存状态
        context.save();
        // 平移
        context.translate(-25, -25);
        // 调用 rect 指令完成绘图
        context.rect(canvas.width/2, canvas.height/2, 50, 50);
        // 恢复状态
        context.restore();
        // 将绘制的内容真正输出到画布中
        context.fill();
    </script>
</body>
</html>


51512c19c4c14c1b8d0970e770d5fbc1.png


如何用 Canvas 绘制层次关系图?

需要实现下面的效果


6ad88123346d404687e61f193e5797ea.png



json数据如下:

export default {
    "name": "中国",
    "children": [
        {
            "name": "浙江",
            "children": [
                {
                    "name": "杭州"
                },
                {
                    "name": "宁波"
                },
                {
                    "name": "温州"
                },
                {
                    "name": "绍兴"
                }
            ]
        },
        {
            "name": "广西",
            "children": [
                {
                    "name": "桂林"
                },
                {
                    "name": "南宁"
                },
                {
                    "name": "柳州"
                },
                {
                    "name": "防城港"
                }
            ]
        },
        {
            "name": "黑龙江",
            "children": [
                {
                    "name": "哈尔滨"
                },
                {
                    "name": "齐齐哈尔"
                },
                {
                    "name": "牡丹江"
                },
                {
                    "name": "大庆"
                }
            ]
        },
        {
            "name": "新疆",
            "children": [
                {
                    "name": "乌鲁木齐"
                },
                {
                    "name": "克拉玛依"
                },
                {
                    "name": "吐鲁番"
                },
                {
                    "name": "哈密"
                }
            ]
        },
        {
            "name": "河北",
            "children": [
                {
                    "name": "石家庄"
                },
                {
                    "name": "唐山"
                },
                {
                    "name": "邯郸"
                },
                {
                    "name": "秦皇岛"
                }
            ]
        },
        {
            "name": "西藏",
            "children": [
                {
                    "name": "拉萨"
                },
                {
                    "name": "昌都"
                },
                {
                    "name": "林芝"
                }
            ]
        },
        {
            "name": "江苏",
            "children": [
                {
                    "name": "南京"
                },
                {
                    "name": "无锡"
                },
                {
                    "name": "徐州"
                },
                {
                    "name": "常州"
                },
                {
                    "name": "连云港"
                },
                {
                    "name": "淮安"
                }
            ]
        },
        {
            "name": "江苏",
            "children": [
                {
                    "name": "南京"
                },
                {
                    "name": "无锡"
                },
                {
                    "name": "徐州"
                },
                {
                    "name": "常州"
                },
                {
                    "name": "连云港"
                },
                {
                    "name": "淮安"
                }
            ]
        },
        {
            "name": "湖南",
            "children": [
                {
                    "name": "长沙"
                },
                {
                    "name": "株洲"
                },
                {
                    "name": "湘潭"
                },
                {
                    "name": "衡阳"
                },
                {
                    "name": "邵阳"
                },
                {
                    "name": "岳阳"
                }
            ]
        },
        {
            "name": "海南",
            "children": [
                {
                    "name": "海口"
                },
                {
                    "name": "三亚"
                },
                {
                    "name": "三沙"
                }
            ]
        },
        {
            "name": "陕西",
            "children": [
                {
                    "name": "西安"
                },
                {
                    "name": "咸阳"
                },
                {
                    "name": "汉中"
                },
                {
                    "name": "安康"
                },
                {
                    "name": "榆林"
                },
                {
                    "name": "延安"
                }
            ]
        },
        {
            "name": "甘肃",
            "children": [
                {
                    "name": "兰州"
                },
                {
                    "name": "酒泉"
                },
                {
                    "name": "金昌"
                },
                {
                    "name": "天水"
                },
                {
                    "name": "嘉峪关"
                },
                {
                    "name": "武威"
                }
            ]
        }
    ]
}


这里使用 d3-hierarchy 工具将数据转为下面的效果

<script src="https://d3js.org/d3-hierarchy.v1.min.js"></script>


{
  data: {name: '中国', children: [...]},
  children: [
    {
      data: {name: '江苏', children: [...]},
      value: 7,
      r: 186.00172579386546,
      x: 586.5048250548921,
      y: 748.2441892254667,
    }
    ...
  ],
  value: 69,
  x: 800,
  y: 800,
  r: 800,
}


代码实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>如何用Canvas绘制层次关系图(2)</title>
    <style>
        canvas {
            width: 800px;
            height: 800px;
        }
    </style>
</head>
<body>
    <canvas width="1600" height="1600"></canvas>
    <script src="https://d3js.org/d3-hierarchy.v1.min.js"></script>
    <script type="module">
        import dataSource from './data/map.js';
        // 用 d3.hierarchy(data).sum(…).sort(…) 将省份数据按照包含城市的数量,从多到少排序
        const regions = d3.hierarchy(dataSource)
            .sum(d => 1)
            .sort((a, b) => b.value - a.value);
        // 通过 d3.pack() 将数据映射为一组 1600 宽高范围内的圆形,留3px的padding
        const pack = d3.pack()
            .size([1600, 1600])
            .padding(3);
        const root = pack(regions);
        console.log(root)
        const canvas = document.querySelector('canvas');
        const context = canvas.getContext('2d');
        function draw(ctx, node, { fillStyle = 'rgba(0, 0, 0, 0.2)', textColor = 'white' } = {}) {
            const children = node.children;
            const { x, y, r } = node;
            ctx.fillStyle = fillStyle;
            ctx.beginPath();
            // arc 方法的五个参数分别是圆心的 x、y 坐标、半径 r、起始角度和结束角度
            ctx.arc(x, y, r, 0, 2 * Math.PI);
            ctx.fill();
            if (children) {
                // 遍历子节点绘制
                for (let i = 0; i < children.length; i++) {
                    draw(context, children[i]);
                }
            } else {
                // 绘制文本
                ctx.fillStyle = textColor;
                ctx.font = '1.5rem Arial';
                ctx.textAlign = 'center';
                ctx.fillText(node.data.name, x, y);
            }
        }
        draw(context, root);
    </script>
</body>
</html>

58caf8c439964d87addf7b19cbed7f77.png


Canvas 有哪些优缺点?

Canvas 在 HTML 层面上是一个独立的画布元素,所以所有的绘制内容都是在内部通过绘图指令来完成的,绘制出的图形对于浏览器来说,只是 Canvas 中的一个个像素点,很难直接抽取其中的图形对象进行操作。


目录
相关文章
|
SQL 关系型数据库 MySQL
gin框架学习-Gorm入门指南
Snake Case命名风格,就是各个单词之间用下划线(_)分隔,首字母大写区分一个单词,例如: CreateTime的Snake Case风格命名为create_time
668 0
gin框架学习-Gorm入门指南
|
6月前
|
存储 Java Maven
Maven系统级别依赖:解决部署时Jar包缺失问题
以上就是关于Maven系统级别依赖解决部署时Jar包缺失问题的解答,希望对你有所帮助。在软件开发中,遇到问题并解决问题是常态,希望你能够善用这些工具,解决你遇到的问题。
333 28
|
缓存 NoSQL Java
Redis深度解析:解锁高性能缓存的终极武器,让你的应用飞起来
【8月更文挑战第29天】本文从基本概念入手,通过实战示例、原理解析和高级使用技巧,全面讲解Redis这一高性能键值对数据库。Redis基于内存存储,支持多种数据结构,如字符串、列表和哈希表等,常用于数据库、缓存及消息队列。文中详细介绍了如何在Spring Boot项目中集成Redis,并展示了其工作原理、缓存实现方法及高级特性,如事务、发布/订阅、Lua脚本和集群等,帮助读者从入门到精通Redis,大幅提升应用性能与可扩展性。
213 0
|
11月前
|
JavaScript 前端开发
在 JavaScript 中动态创建插槽内容
【10月更文挑战第26天】在JavaScript中动态创建插槽内容有多种方法,从简单的字符串拼接和DOM操作到使用流行的框架如Vue.js和React等。选择哪种方法取决于项目的具体需求和所使用的技术栈。在简单的静态页面中,原生的JavaScript DOM操作可能就足够了;而在复杂的单页应用中,使用框架可以提供更好的开发体验、性能优化和代码组织。
|
机器学习/深度学习 监控 物联网
函数计算操作报错合集之调用接口提示Cannot copy out of meta tensor; no data! 是什么原因
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
670 0
|
12月前
|
存储 消息中间件 druid
大数据-151 Apache Druid 集群模式 配置启动【上篇】 超详细!
大数据-151 Apache Druid 集群模式 配置启动【上篇】 超详细!
220 1
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
4758 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
算法 数据挖掘 vr&ar
基于ESTAR指数平滑转换自回归模型的CPI数据统计分析matlab仿真
该程序基于ESTAR指数平滑转换自回归模型,对CPI数据进行统计分析与MATLAB仿真,主要利用M-ESTAR模型计算WNL值、P值、Q值及12阶ARCH值。ESTAR模型结合指数平滑与状态转换自回归,适用于处理经济数据中的非线性趋势变化。在MATLAB 2022a版本中运行并通过ADF检验验证模型的平稳性,适用于复杂的高阶自回归模型。
|
JavaScript
vue中关于element的el-image 图片预览功能增加一个下载按钮
vue中关于element的el-image 图片预览功能增加一个下载按钮
1450 0
|
SQL 关系型数据库 数据库连接
Python连接线上数据库的实战指南
Python连接线上数据库的实战指南
900 1