介绍
做过数据可视化的同学对于HIGHCHARTS可能并不陌生,甚至是家族的其他两个成员 HIGHSTOCK 和 HIGHMAPS 也使用过
它对新老浏览器的兼容性和响应式特性是我们偏爱使用它的主要原因,当然文档齐全和demo丰富也是它的特点
下面是它的兼容性表格:
Brand |
Versions supported |
Internet Explorer |
6.0 + |
Firefox |
2.0 + |
Chrome |
1.0 + |
Safari |
4.0 + |
Opera |
9.0 + |
iOS (Safari) |
3.0 + |
Android Browser |
2.0 + |
基本覆盖了主要的桌面和移动浏览器,即使在天朝也可以放心使用,在IE9以下版本的浏览器会采用VML的降级方式渲染图形,在Android 2.X的原生浏览器会使用Canvas渲染图形
使用非商用证书可以免费使用 Highchart 的产品,如果商用的话,就要花费一笔购买许可证书之后再使用
使用API绘制图表、地图等等再这里就不细说了,绘制自定义图形是重点
当然,使用 d3, raphael, sylvester, ocanvas, two.js, svg.js, snap.svg 等图形库也可以帮助你绘制自定义图形,但是鉴于 HIGHCHART 的诸多优点,我习惯使用它
进入正题
Renderder
Renderer
这个构造器是绘制的核心,创建这个构造器的实例可以采用下面的方法
1
| var renderer = new Highcharts.Renderer(parentNode, width, height);
|
当调用 highcharts
方法时内部的 this.renderer
就是一个 Renderer
构造器的实例
1 2 3 4 5 6 7 8 9 10 11
| $('#container').highcharts({ chart: { backgroundColor: 'white', events: { load: function () { var ren = this.renderer, } } } });
|
在 highcharts-core 源码里我们可以看到,在Renderder的原型方法中,我们可以独立于 Chart 单独绘图的方法包括:
- path : 绘制路径
- circle : 绘制圆形
- arc : 绘制拱形
- rect : 绘制矩形
- g : 创建用于把相关元素进行组合的容器元素
- image : 显示图片
- text : 显示文字
- label : 绘制一个具有阴影、边框、(渐变)背景色的 Label
- button: 绘制一个具有相应状态(hover, active .etc)的按钮,并可以绑定点击事件的回调函数
element
Element 构造器是 highchart 里 svg 元素 的 constructor
,它也提供了一些实用的原型方法供实例调用:
- add : 像渲染画布中添加元素
- animate : 对 svg/vml 元素使用动画效果,方法同 jQuery
animate()
方法
- attr : 为 svg/vml 元素添加属性,方法同 jQuery
attr
方法
- css : 不罗嗦。。同 jQuery
- destroy : 删除元素并释放内存
- on : 为元素绑定事件处理函数
- toFront : 前置元素
一言不合, 开始编码
以NCZ大师那篇划时代的前后端分离文章的配图为目标,用 Renderer 实例来绘制相同的原理图
为了节省时间只画了上半部分
绘制原理:
添加前端部分的分组
1 2
| var frontEnd = render.g().add();
|
用 rect()
方法添加前端部分的底层画布,其实就是先声明一个矩形,由于 SVG 没有 z-index
的概念,因此绘制的顺序决定了元素是否显示(即覆盖与否),使用 highcharts 元素内置的 toFront()
方法可以让元素前置显示,但是在编写时保证清晰的顺序是最好的
1 2 3 4
| var FECanvas = render.rect(0, 0, 600, 360).attr({ 'fill': '#FDEADA' }).add(frontEnd);
|
生成左侧文字,添加到分组,注意 text()
方法的后两个参数为 x 和 y 坐标
1 2 3 4
| render.text('Front-End', 45, 164).attr({ 'font-size': '19px', 'font-weight': 'bold' }).add(frontEnd);
|
为第一个 label 创建分组,来盛放 chrome 那张 logo 和里面那个有边框的 label ,使用 image()
方法引入图片,再用 rect
和 text
组合的方式生成 Layer UI 的 label
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| var FEGrah = render.g().add(frontEnd);
render.rect(240, 25, 341, 88, 2).attr({ fill: '#fff', stroke: '#A09286', 'stroke-width': '6px', 'stroke-opacity': '0.3' }).add(FEGrah);
render.rect(240, 25, 340, 86, 2).attr({ 'fill': '#4F81BD', 'stroke': '#fff', 'stroke-width': '4px' }).add(FEGrah);
render.image('http://upload.wikimedia.org/wikipedia/commons/8/87/Google_Chrome_icon_(2011).png', 250, 30, 75, 75) .add(FEGrah);
render.rect(353, 46, 212, 47, 2).attr({ fill: '#A09286', stroke: '#A09286', 'stroke-width': '6px', 'stroke-opacity': '0.3' }).add(FEGrah);
render.rect(353, 46, 210, 46, 2).attr({ 'fill': '#9BBB59', 'stroke': '#fff', 'stroke-width': '4px' }).add(FEGrah);
render.text('UI Layer', 425, 75).attr({ 'font-size': '19px' }).css({ 'color': '#fff' }).add(FEGrah);
render.text('Browser', 360, 140).attr({ 'font-size': '19px', 'font-weight': 'bold' }).add(FEGrah);
|
下图 nodejs logo 所在的 label 采用了 label()
方法来生成,传入文字和坐标,在 attr() 和 css()
方法里指定一些样式让生成的label更美观
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| render.label('UI Layer', 353, 257).attr({ fill: "#F79646", stroke: 'white', 'stroke-width': 4, padding: 12, r: 2, width: 185 }).css({ textAlign: 'center', color: '#fff', fontSize: '19px'
}).add(FEGrah2)
.shadow(true);
|
箭头部分是难点,箭头采用了 path()
方法绘制,定义坐标系原点,然后通过路径数组来自定义路径轨迹,箭头1便是这么画出来的,传入数组:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
var arrowPath = ['m', 370, 142, 'l', -25, 36, 13, 0, 0, 55, 24, 0, 0, -55, 13, 0, -25, -36];
var narrow = render.path(arrowPath).attr({ stroke: '#EC9694', 'stroke-width': '2', fill: 'pink' }).add(FENarrow);
|
为了节省精力,右边颠倒的箭头我们采用偷懒的方式,复制左边的箭头,然后把复制的箭头放进一个分组中,然后对这个单独的分组添加属性 transform
1 2 3 4 5 6 7 8 9 10 11 12
| var narrow2 = render.path(arrowPath).attr({ stroke: '#EC9694', 'stroke-width': '2', fill: 'pink' }).add(FENarrow2);
FENarrow2.attr({ 'transform': 'rotate(-180 397 189)' });
|
demo展示
SVG展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <svg version="1.1" style="font-family:"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif;font-size:12px;" xmlns="http://www.w3.org/2000/svg" width="600" height="660"> <desc>Created with Highcharts 4.0.3</desc> <defs></defs> <g> <rect x="0" y="0" width="600" height="360" fill="#FDEADA"></rect> <text x="45" y="164" font-size="19px" font-weight="bold">Front-End</text> <g> <rect x="240" y="25" width="341" height="88" rx="2" ry="2" fill="#fff" stroke="#A09286" stroke-width="6px" stroke-opacity="0.3"></rect> <rect x="240" y="25" width="340" height="86" rx="2" ry="2" fill="#4F81BD" stroke="#fff" stroke-width="4px"></rect> <image preserveAspectRatio="none" x="250" y="30" width="75" height="75" xlink:href="http://upload.wikimedia.org/wikipedia/commons/8/87/Google_Chrome_icon_(2011).png"></image> <rect x="353" y="46" width="212" height="47" rx="2" ry="2" fill="#A09286" stroke="#A09286" stroke-width="6px" stroke-opacity="0.3"></rect> <rect x="353" y="46" width="210" height="46" rx="2" ry="2" fill="#9BBB59" stroke="#fff" stroke-width="4px"></rect> <text x="425" y="75" font-size="19px" style="color:#fff;fill:#fff;"> <tspan>UI Layer</tspan> </text> <text x="360" y="140" font-size="19px" font-weight="bold">Browser</text> </g> <g> <rect x="240" y="240" width="341" height="88" rx="2" ry="2" fill="#fff" stroke="#A09286" stroke-width="6px" stroke-opacity="0.3"></rect> <rect x="240" y="240" width="340" height="86" rx="2" ry="2" fill="#4F81BD" stroke="#fff" stroke-width="4px"></rect> <image preserveAspectRatio="none" x="245" y="255" width="100" height="55" xlink:href="http://nodejs.org/images/logos/nodejs.png"></image> <g style="text-align:center;" transform="translate(353,257)"> <rect x="0" y="0" width="209" height="47" strokeWidth="4" fill="none" stroke="black" stroke-width="5" rx="2" ry="2" isShadow="true" stroke-opacity="0.049999999999999996" transform="translate(1, 1)"></rect> <rect x="0" y="0" width="209" height="47" strokeWidth="4" fill="none" stroke="black" stroke-width="3" rx="2" ry="2" isShadow="true" stroke-opacity="0.09999999999999999" transform="translate(1, 1)"></rect> <rect x="0" y="0" width="209" height="47" strokeWidth="4" fill="none" stroke="black" stroke-width="1" rx="2" ry="2" isShadow="true" stroke-opacity="0.15" transform="translate(1, 1)"></rect> <rect x="0" y="0" width="209" height="47" strokeWidth="4" fill="#F79646" stroke="white" stroke-width="4" rx="2" ry="2"></rect> <text x="67.5" zIndex="1" style="font-size:19px;color:#fff;fill:#fff;" y="30"> <tspan>UI Layer</tspan> </text> </g> <text x="375" y="354" font-size="19" font-weight="bold">Server</text> </g> <g> <path fill="pink" d="m 370 142 l -25 36 13 0 0 55 24 0 0 -55 13 0 -25 -36" stroke="#EC9694" stroke-width="2"></path> </g> <g transform="rotate(-180 397 189)"> <path fill="pink" d="m 370 142 l -25 36 13 0 0 55 24 0 0 -55 13 0 -25 -36" stroke="#EC9694" stroke-width="2"></path> </g> </g> <text x="455" y="195" font-size="19" font-weight="bold">HTTP/HTTPS</text> </svg>
|
参考链接:
http://www.w3.org/TR/SVG/propidx.html
http://api.highcharts.com/highcharts#Renderer
Best wish!
本文地址:
https://mrpeak.github.io/2014/09/06/highchart-svg/