前端数据可视化第一篇,基础的svg和canvas
svg
SVG是一种基于XML语法的图象格式,全称是可缩放矢量图,其他图像格式都是基于像素处理的,SVG则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。
SVG元素可以直接插入网页,成为DOM的一部分,然后用JavaScript和css进行操作
1 |
|
基础标签
api
circle标签
circle标签代表圆形,cx、cy、r属性分别为横坐标、纵坐标、和半径单位为像素。
1 | <svg width="300" height="300"> |
line标签代表直线
1 | <svg width="300" height="300"> |
polyline标签用于绘制一根折线
1 | <svg width="300" height="300"> |
rect标签用于绘制矩形
1 | <svg width="300" height="300"> |
ellipse标签用于绘制椭圆
1 | <svg width="300" height="180"> <ellipse cx="60" cy="60" ry="40" rx="20" stroke="black" stroke-width="5" fill="silver"/></svg> |
polygon标签用于绘制多边形
1 | <svg width="300" height="180"> <polygon fill="green" stroke="orange" stroke-width="1" points="0,0 100,0 100, 100 0,100 0, 0"/></svg> |
path标签用于绘制路径
1 | <svg width="300" height="300"> <path d=" M 18,3 L 46,3 L 46,40 L 62,40 L 32,68 L 3,40 L 18,40 Z " > </path></svg> |
marker-start属性
Marker-end属性
stroke-linecap属性:用于控制描边末端的样式
Stroke-dasharray属性:控制画笔的虚实,通过实线和虚线控制画
stroke-dashoffset属性:相对于绘制的起点偏移的量,正值是向右或者顺时针偏移,负值是向左或者逆时针偏移
text标签用于绘制文本
1 | <svg width="300" height="300"> <text x="50" y="25">22</text></svg> |
use标签用于复制一个形状
1 | <svg viewBox="0 0 30 10" xmlns="https://www.w3.org/2000/svg"> <circle id="myCircle" cx="5" cy="5" r="4" /> <use href="#myCircle" x="10" y="0" fill="blue"/> <use href="#myCircle" x="20" y="0" fill="white" stroke="blue" /></svg> |
g标签用于将多个形状组成一个组,方便复用
1 | <svg width="300" height="300"> <g id="myCircle"> <text></text> <circle cx="50" cy="50" r="20"/> </g></svg> |
defs标签用于自定义形状,它内部的代码不会显示,仅供饮用
1 | <svg width="300" height="100"> <defs> <g id="myCircle"> <text x="25" y="20"></text> <circle cx="50" cy="50" r="20"/> </g> </defs> <usr href="#myCircle" x="0" y="0"/> <usr href="#myCircle" x="100" y="0" fill="blue"/> <usr href="#myCircle" x="200" y="0" fill="white" stroke="blue"/></svg> |
pattern标签用于自定义一个形状,该形状可以被引用来平铺一个区域
1 | <svg width="500" height="500"> <defs> <pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse"> <circle fill="#bee9e8" cx="50" r="35"/> </pattern> </defs> <rect x="0" y="0" width="100%" height="100%" fill="url(#dots)" /></svg> |
image标签用于插入图片文件
1 | <svg viewBox="0 0 100 100" width="100" height"100"> <image xlink:href="path/to/image.jpg" width="50%" height="50%"/></svg> |
animate标签用于产生动画效果
1 | <svg width="500px" height="500px"> <rect x="0" y="0" width="100" height="100" fill="#feac5e"> <animate attributeName="x" from="0" to="500" dur="2s" repeatCount="indefinite" /> </rect></svg> |
animateTransform
1 | <svg width="500px" height="500px"> <rect x="250" y="250" width="50" height="50" fill="#4bc0c8"> <animateTransform attributeName="transform" type="rotate" begin="0s" dur="10s" from="0 200 200" to="360 400 400" repeat="indefinite" /> </rect></svg> |
webapi
SVGGElement对应g标签
SVGDefsElement对应defs标签
SVGEllipseElement对应ellipse标签
https://developer.mozilla.org/en-US/docs/Web/API/SVGGElement
js操作svg
1 | <html><body> <svg id="mysvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet" > <circle id="myCircle" cx="400" cy="300" r="50" /> </svg></body><css type="text/css"> circle { stroke-width: 5; stroke: #f00; fill: #ff0; } circle:hover { stroke: #090; fill: #fff; }<css><script type="text/javascript"> var mycircle = document.getElementById('mycircle'); mycircle.addEventListener('click',function(e) { console.log('circle clicked - enlarging'); mycircle.setAttribute('r',60); },false)</script></html> |
svg常见用法
作为css背景图
描边动画
1 | <svg width="100%" height="100"> <text text-anchor="middle" x="50%" y="50%" class="text text-1"> segmentfault.com </text> <text text-anchor="middle" x="50%" y="50%" class="text text-2"> segmentfault.com </text> <text text-anchor="middle" x="50%" y="50%" class="text text-3"> segmentfault.com </text> <text text-anchor="middle" x="50%" y="50%" class="text text-4"> segmentfault.com </text></svg><css>.text { font-size: 64px; font-weight: bold; text-transform: uppercase; fill: none; stroke-width: 2px; stroke-dasharray: 90 310; animation: stroke 6s infinite linear;}.text-1 { stroke: #3498db; text-shadow: 0 0 5px #3498db; animation-delay: -1.5s;}.text-2 { stroke: #f39c12; text-shadow: 0 0 5px #f39c12; animation-delay: -3s;}.text-3 { stroke: #e74c3c; text-shadow: 0 0 5px #e74c3c; animation-delay: -4.5s;}.text-4 { stroke: #9b59b6; text-shadow: 0 0 5px #9b59b6; animation-delay: -6s;}@keyframes stroke { 100% { stroke-dashoffset: -400; } }</css> |
圆形进度条
1 | <svg> <circle cx="150" cy="73" r="60" stroke="grey" stroke-width="12" fill="none" stroke-dasharray="190" stroke-dashoffset="-190" stroke-linecap="round" /> <circle cx="150" cy="73" r="60" stroke="gold" stroke-width="12" fill="none" stroke-dasharray="95 190" #修改此数字即为进度条进度 stroke-dashoffset="-190" stroke-linecap="round" /></svg> |
插入class
svg元素可以插入class,用css控制元素
canvas api
<canvas>
元素用于生成图像。它本身就像一个画布,JavaScript 通过操作它的 API,在上面生成图像。它的底层是一个个像素,基本上<canvas>
是一个可以用 JavaScript 操作的位图(bitmap)
它与 SVG 图像的区别在于,<canvas>
是脚本调用各种方法生成图像,SVG 则是一个 XML 文件,通过各种子元素生成图像。
每个<canvas>
元素都有一个对应的CanvasRenderingContext2D
对象(上下文对象)。Canvas API 就定义在这个对象上面。
1 | <canvas id="myCanvas" width="400" height="250"> |
Canvas API 需要getContext
方法指定参数2d
,表示该<canvas>
节点生成 2D 的平面图像。如果参数是webgl
,就表示用于生成 3D 的立体图案,这部分属于 WebGL API。
方法:
绘制路径:
ctx.beginPath()
:
ctx.closePath()
:
ctx.moveTo()
:
ctx.lineTo()
:
ctx.fill()
:
ctx.stroke()
:
ctx.fillStyle()
:
ctx.strokeStyle
:
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
:
ctx.restore()
方法将画布的样式恢复到上一个保存的快照,如果没有已保存的快照,则不产生任何效果。
图像变换:
ctx.rotate
:图像旋转
ctx.scale
:图像缩放
ctx.translate
:图像平移
ctx.transform
:设置图像变换
ctx.settransform
:取消前面的图像变换
echart
高效率绘制各种美观图表
安装echart
1 | npm install echarts --save |
安装完成后 ECharts 和 zrender 会放在 node_modules 目录下
引入echart
1 | import echarts from "echarts" |
基础的数据可视化
基本名词解释
名词 | 描述 |
---|---|
chart | 是指一个完整的图表,如折线图,饼图等“基本”图表类型或由基本图表组合而成的“混搭”图表,可能包括坐标轴、图例等 |
axis | 直角坐标系中的一个坐标轴,坐标轴可分为类目型、数值型或时间型 |
xAxis | 直角坐标系中的横轴,通常并默认为类目型 |
yAxis | 直角坐标系中的纵轴,通常并默认为数值型 |
grid | 直角坐标系中除坐标轴外的绘图网格,用于定义直角系整体布局 |
legend | 图例,表述数据和图形的关联 |
dataRange | 值域选择,常用于展现地域数据时选择值域范围 |
dataZoom | 数据区域缩放,常用于展现大量数据时选择可视范围 |
roamController | 缩放漫游组件,搭配地图使用 |
toolbox | 辅助工具箱,辅助功能,如添加标线,框选缩放等 |
tooltip | 气泡提示框,常用于展现更详细的数据 |
timeline | 时间轴,常用于展现同一系列数据在时间维度上的多份数据 |
series | 数据系列,一个图表可能包含多个系列,每一个系列可能包含多个数据 |
图表名词
名词 | 描述 |
---|---|
line | 折线图,堆积折线图,区域图,堆积区域图。 |
bar | 柱形图(纵向),堆积柱形图,条形图(横向),堆积条形图。 |
scatter | 散点图,气泡图。散点图至少需要横纵两个数据,更高维度数据加入时可以映射为颜色或大小,当映射到大小时则为气泡图 |
k | K线图,蜡烛图。常用于展现股票交易数据。 |
pie | 饼图,圆环图。饼图支持两种(半径、面积)南丁格尔玫瑰图模式。 |
radar | 雷达图,填充雷达图。高维度数据展现的常用图表。 |
chord | 和弦图。常用于展现关系数据,外层为圆环图,可体现数据占比关系,内层为各个扇形间相互连接的弦,可体现关系数据 |
force | 力导布局图。常用于展现复杂关系网络聚类布局。 |
map | 地图。内置世界地图、中国及中国34个省市自治区地图数据、可通过标准GeoJson扩展地图类型。支持svg扩展类地图应用,如室内地图、运动场、物件构造等。 |
heatmap | 热力图。用于展现密度分布信息,支持与地图、百度地图插件联合使用。 |
gauge | 仪表盘。用于展现关键指标数据,常见于BI类系统。 |
funnel | 漏斗图。用于展现数据经过筛选、过滤等流程处理后发生的数据变化,常见于BI类系统。 |
evnetRiver | 事件河流图。常用于展示具有时间属性的多个事件,以及事件随时间的演化。 |
treemap | 矩形式树状结构图,简称:矩形树图。用于展示树形数据结构,优势是能最大限度展示节点的尺寸特征。 |
venn | 韦恩图。用于展示集合以及它们的交集。 |
tree | 树图。用于展示树形数据结构各节点的层级关系 |
wordCloud | 词云。词云是关键词的视觉化描述,用于汇总用户生成的标签或一个网站的文字内容 |
实例
1 | //基于准备好的dom,初始化echart图表 |
echarts vs d3
D3 的全称是(Data-DrivenDocuments),顾名思义是一个被数据驱动的文档。其实是对数据进行可视化的 JavaScript 库。D3 将强大的可视化和交互技术与数据驱动的 DOM 操作方法相结合,能最大限度地使用现代浏览器的性能同时为设计可视化界面保留了最大的自由度。D3 强调 Web 标准,所以无需将自己与专有框架联系起来
ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器,底层依赖轻量级的矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。ECharts 提供完善的中文文档,能够快速通过配置实现支持的图表。
开发流程
对于 ECharts,hightchart 这类经过进一步封装的可视化库开发流程
- 根据需求确定图表类型 (例如折线 d3.line)
- 查找所需图表类型配置和数据格式要求,按照要求进行配置项配置,转换数据到指定格式
- 在该类图表支持的动画、事件交互中选择需要的进行配置
对于 D3
- 根据需求确定图表类型 (例如折线 d3.line)
- 把输入的原始数据转化成为标准的 D3 可接受的数据格式一般图像是对象数组
- 根据原始数据定义好 x 轴函数、y 轴函数和定义好作图方式在 SVG 上,画出 x 轴 y 轴、根据原始数据结合 x 轴及 y 轴函数作线状图
- 再画出标题等细节的东西
- 给已经完成的图形添加动画效果和事件交互
ECharts,hightchart 这类可视化库,经过配置即可完成图表的绘制,D3 要稍微麻烦一点。
使用 D3 绘图需要
- 熟悉 SVG(canvas)作图、熟悉 CSS
- 熟悉 D3 的 API
- 学习 D3.js 的编程风格
对比 D3 和 ECharts 之前,先对比下两种浏览器图形渲染技术 Canvas 和 SVG 的主要区别,基本上所有的浏览器可视化第三方库都是基于这两种浏览器图形渲染技术实现的(WebGl 虽然和 2D 的 canvas 没啥关系,广义上也算 Canvas):
SVG | Canvas |
---|---|
矢量图不依赖分辨率,放大不失真 | 位图依赖分辨率 |
基于 XML 支持 DOM 事件处理器,SVG 中每个图形节点都是单独的 DOM 节点可以附加 js 事件 | 不支持事件处理器 如果要为细粒度的元素添加事件,就需要边缘检测算法,无疑增加了难度而且不一定能保证十分精确 |
适合带有大型渲染区域的应用(比如:地图(每个节点区域都比较大,节点不是很多)) | 最适合图像密集型的应用比如游戏 能够方便的保存结果图像 |
复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都快不了) | 一旦图形绘制完成,就不会再得到浏览器的关注 如果位置或者大小变化整个区域都要重新绘制 |
可以使用 CSS 文本渲染能力较强 | 文本渲染能力较弱 |
简单对比后:
SVG 比较适合 数据量不是很大,应用存在大量的用户交互场景。
Canvas 比较适合事件交与较少,文本较少,或者数据量大画面刷新快的场景。
ECharts | D3 |
---|---|
Canvas 为主 4.0+ 也支持 SVG | SVG 为主 4.0+ 也支持 Canvas |
提供很多图表通过简单配置可以满足大部分需求 | 不能通过简单配置实现大部分图表 |
基本不可定制 | 自由度很大,基本可以自己绘制任何图表 |
提供完善中文文档,示例功能完善 | 提供完善英文文档,3.x 版本中文文档比较完善,之后翻译的不完善,部分翻译可能需求查看英文方便理解,大部分示例需要完善才能使用,主要是参考价值 |
开发效率高,通过快速配置即可完成 | 大部分图表需要开发 |
大数据量性能较好 | 需要实现时手动优化 例如 virtual DOM |
提供基于 WebGl 的 GL 版实现 3D 图表 | 借助其他库来实现例如:three.js、glMatrix、Sylvester |
ECharts 等提供的图表的确可以满足大部分的需求,遵循了数据可视化的一些经典范式,一切皆可配置。然而,每个不同的行业对于数据可视化都会有一些定制化的需求,希望能以一些带有行业特征的图表向使用者展示数据背后隐藏的秘密,但是 ECharts 这类图形库基本不可定制,而 D3 自由度度很大,基本可以自己绘制任何想要的图形,这类情况的需求可以使用 D3 进行二次开发,定制适合的图表,但是开发成本会稍高。因此,开发中要根据实际情况来判断。无论采用哪种方式开发都要做好二次封装,把实现的图表成可复用的组件。
对于 ECharts 支持的图表:
使用 ECarts 有较大的优势,开发效率高,动画、事件等实现也比较完善,正常情况下基本没有 bug。
对于 Echarts 不支持的图表:
- 数据量不是特别大或者事件交互比较精细的场景采用 D3 SVG 来实现,可以先在官方示例和 demo 搜索有没有类似的图表实现。
对于频繁的 DOM 操作十分消耗性能。对于用户体验的影响便是可能出现闪烁、卡顿等现象。可以参考前端界对于页面 DOM 卡顿的解决方案:Virtual DOM 技术。通过支持 Virtual Dom 技术的框架如 Vue 与 D3.js 结合。使用 D3 来计算,Vue 等 Virtual DOM 框架管理 SVG 节点和属性。 - 对于数据量比较大的场景,可以采用 D3Canvas 来实现,或者ZRender(ECharts 使用的矢量图形库)来定制,这个需要比较熟悉 Canvas 绘图,而且需要注意性能的优化。
- 数据量不是特别大或者事件交互比较精细的场景采用 D3 SVG 来实现,可以先在官方示例和 demo 搜索有没有类似的图表实现。