轻量级的Canvas类库zrender使用笔记 :简单自定义图件开发
2017-12-20 16:32
3367 查看
ECharts,一个纯 Javascript 的图表库,底层依赖轻量级的 Canvas 类库 ZRender,提供直观,生动,可交互,可高度个性化定制的数据可视化图表。当然我们自己可能有些需求,通过修改ECharts或者highcharts的option不能实现,比如说宽度不一致的柱状图图件。可以直接使用Canvas类库zrender开发图件,或者使用snap.svg.js开源项目开发图件。这里写了一个demo使用轻量级的Canvas类库zrender,自定义一个简单的图件。
ZRender 是二维绘图引擎,它提供 Canvas、SVG、VML 等多种渲染方式。ZRender 也是 ECharts 的渲染器。先来看看zrender的总体结构。
zrender使用起来非常方便,这里简单说几点用法,其他的用法可自行查看zRender的官方文档。
1、添加矩形,注意默认的填充颜色是黑色
2、矩形使用线性渐变色填充
3、矩形添加动画,矩形的左上角从位置(10,10)移动到(10,100)。也就是纵向向下移动90px
4、绘制一条虚线,加上动画,在0.5秒的时间里绘制从0%到100%
lineDash属性,也就是虚线样式
5、矩形添加添加鼠标事件
on(eventName, eventHandler, context)
绑定事件处理。
下面写了一个简单的demo,使用zrender开发的简单图件。同时将它封装成一个jQuery的插件,方便调用。按照不同的段绘制渐变色的柱状图,同时点击的时候能够动画显示数值。把它命名为demo.js。
ZRender 是二维绘图引擎,它提供 Canvas、SVG、VML 等多种渲染方式。ZRender 也是 ECharts 的渲染器。先来看看zrender的总体结构。
zrender使用起来非常方便,这里简单说几点用法,其他的用法可自行查看zRender的官方文档。
1、添加矩形,注意默认的填充颜色是黑色
var rect = new zrender.Rect({ shape: { x: 0, y: 0, width: 100, height:100 }, style: { stroke:'#ffc8aa' }, position: [10,10] }); zr.add(rect );
2、矩形使用线性渐变色填充
var linearColor = new zrender.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#efe3ff' }, { offset: 1, color: '#6cb3e9' } ]); var rect = new zrender.Rect({ shape: { x: 0, y: 0, width: 100, height:100 }, style: { fill:linearColor }, position: [10,10] }); zr.add(rect );
3、矩形添加动画,矩形的左上角从位置(10,10)移动到(10,100)。也就是纵向向下移动90px
var rect = new zrender.Rect({ shape: { x: 0, y: 0, width: 100, height:100 }, style: { stroke:'#ffc8aa' }, position: [10,10] }); rect.animateTo({ position: [10,100] }, 500, 0, 'linear'); zr.add(rect );
4、绘制一条虚线,加上动画,在0.5秒的时间里绘制从0%到100%
var line = new zrender.Line({ shape: { x1:10, y1:10, x2:100, y2:10, percent:0 }, style: { stroke:'#434348', lineDash:[5,5] } }); line.animate('shape', false) .when(500, { percent: 1 }).start(); zr.add(line);
lineDash属性,也就是虚线样式
opts.style.lineDash | number[] | null | 描边虚线样式,参考 SVG stroke-dasharray。 |
var rect = new zrender.Rect({ shape: { x: 0, y: 0, width: 100, height:100 }, style: { stroke:'#ffc8aa' }, position: [10,10] }); rect.on('click',function(){ console.log('单击了这个矩形'); }); zr.add(rect );
on(eventName, eventHandler, context)
绑定事件处理。
参数
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
eventName | string | 事件名称,支持: 'click'、 'mousedown'、 'mouseup'、 'mousewheel'、 'dblclick'、 'contextmenu'。 | |
eventHandler | Function | 事件处理的回调函数。 | |
context | Object | 函数上下文。 |
demo.js
/** * Created by ChenCen on 2017/12/20 */ (function (factory) { if (typeof define === 'function' && define.amd) { define('viewer', ['jquery'], factory); } else if (typeof exports === 'object') { factory(require('jquery')); } else { factory(jQuery); } })(function ($) { 'use strict'; var $window = $(window); var $document = $(document); // Constants var NAMESPACE = 'dircard'; var ELEMENT_VIEWER = document.createElement(NAMESPACE); function isUndefined(u) { return typeof u === 'undefined'; } function isNumber(n) { return typeof n === 'number' && !isNaN(n); } function isString(s) { return typeof s === 'string'; } function toArray(obj, offset) { var args = []; if (isNumber(offset)) { // It's necessary for IE8 args.push(offset); } return args.slice.apply(obj, args); } //globle var stroke="#C0D0E0"; function dirCard(element, options) { this.element=element; this.$element = $(element); this.options =$.extend({}, dirCard.DEFAULTS, options); this.zr = zrender.init(this.element); this.w= this.zr.getWidth(); this.h = this.zr.getHeight(); this.disLeft=0.1; this.disTop=0.1; this.disBottom=0.1; this.disRight=0.05; this.zrEleArray=[]; this.preZrEle=''; this.originLinearColor=''; this.init(); } dirCard.DEFAULTS={ data:[], wellSec:'段', //展示的列 showCol:'', //柱颜色 barColor:['#48c15e','#dff0d8'], //选中颜色 checkColor:['#ff5454','#FF8053'], //背景色 backgroundColor:'#fff', //绘制完成后的回调函数 rowAfter:false }; dirCard.prototype = { constructor: dirCard, //初始化 init: function () { var options = this.options; var beginSec, endSec, length; var sec = [options.wellSec]; var minData, maxData, i; var data = options.data; var showCol = options.showCol; this.length = length = data.length; if (!length || length == 0 || (!data[0][sec])) return; if (!data[0][showCol]) { console.log('provide the wrong clunmn name!'); return } if ((data[0][sec]).split('-').length == 2) { this.split = "-"; beginSec = (data[0][sec]).split('-')[0]; endSec = (data[length - 1][sec]).split('-')[1]; } else if ((data[0][sec]).split('~').length == 2) { this.split = "~"; beginSec = (data[0][sec]).split('~')[0]; endSec = (data[length - 1][sec]).split('~')[1]; } if (beginSec && Number(beginSec) < Number(endSec)) { this.beginSec = Number(beginSec); this.endSec = Number(endSec); minData = Number(data[0][showCol]); maxData = Number(data[0][showCol]); for (i = 1; i < length; i++) { if (minData > Number(data[i][showCol])) minData = Number(data[i][showCol]); if (maxData < Number(data[i][showCol])) maxData = Number(data[i][showCol]); } this.maxData = maxData; this.minData = minData; console.log('max:' + this.beginSec + ' min:' + this.endSec); this.drawBG(); if(maxData==0&&maxData==0) { console.log('all the value is zero'); } else if(maxData<0||minData<0) { console.log('not handle this'); } else { this.drawEle(); } // callBack after draw if (options.rowAfter) { options.rowAfter(); } } else { console.log('there must be something wrong!'); } }, //draw background drawBG: function () { var zr = this.zr; var w = this.w; var h = this.h; var showCol = this.options.showCol; var backgroundColor=this.options.backgroundColor; var disLeft=this.disLeft*w; var disRight=this.disRight*w; var disTop=this.disTop*h; var disBottom=this.disBottom*h; var i; var dis=this.dis=((this.endSec-this.beginSec)/4).toFixed(0); var wRadio=(w-disLeft-disRight)/(this.endSec-this.beginSec); console.log(dis); var bg = new zrender.Rect({ shape: { cx: 0, cy: 0, width: w, height: h }, style: { fill:backgroundColor } /*zlevel: -1*/ }); zr.add(bg); var roundRect = new zrender.Rect({ shape: { cx: 0, cy: 0, width: 0.98*w, height:0.98*h }, style: { stroke:stroke, fill:'#fff', }, position: [0.01*w,0.01*h] }); zr.add(roundRect); //axis var xline =new zrender.Line({ shape: { x1:disLeft, y1:h-disTop, x2:disLeft+wRadio*(4*dis), y2:h-disTop }, style: { stroke:stroke } }); var yline =new zrender.Line({ shape: { x1:disLeft, y1:disTop, x2:disLeft, y2:h-disTop }, style: { stroke:stroke } }); zr.add(xline); zr.add(yline); for(i=0;i<5;i++) { var smline =new zrender.Line({ shape: { x1:0, y1:0, x2:0, y2:0.02*h }, style: { stroke:stroke }, position: [disLeft+wRadio*(i*dis), h-disBottom] }); var smText = new zrender.Text({ style: { stroke: '#434348', text:this.beginSec+(i*dis), fontSize: '11', textAlign:'center' }, position: [disLeft+wRadio*(i*dis), h-disBottom+0.03*h] }); zr.add(smline); zr.add(smText); } }, //draw all ele drawEle: function () { var self = this; var options = this.options; var showCol = options.showCol; var sec=options.wellSec; var color=options.barColor; var zr = this.zr; var w = this.w; var h = this.h; var disLeft=this.disLeft*w; var disRight=this.disRight*w; var disTop=this.disTop*h; var disBottom=this.disBottom*h; var i; var wRadio=(w-disLeft-disRight)/(this.endSec-this.beginSec); var hRadio=(h-disTop-disBottom)/(this.maxData-this.minData); for (i = 0; i < this.length; i++) { var barValue=Number(options.data[i][showCol]); var bg = (options.data[i][sec]).split(this.split)[0]; bg = Number(bg); var ed = (options.data[i][sec]).split(this.split)[1]; ed = Number(ed); this.originLinearColor = new zrender.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: color[0] }, { offset: 1, color: color[1] } ]); var zrEle = new zrender.Rect({ shape: { cx: 0, cy: 0, width: wRadio * (ed - bg), height:0 }, style: { fill: this.originLinearColor }, position: [disLeft+wRadio*(bg-this.beginSec),h-disBottom] // silent: true 不响应鼠标事件 }); zrEle.rowIndex=i; //bind click event self.clickEle(zrEle); zrEle.animateTo({ shape: { cx: 0, cy: 0, width: wRadio * (ed - bg), height:hRadio*(barValue-this.minData) }, position: [disLeft+wRadio*(bg-this.beginSec),h-disBottom-hRadio*(barValue-this.minData)] }, 500, i * 100, 'linear'); zr.add(zrEle); this.zrEleArray.push(zrEle); } }, //click and change style for one ele activeEle: function (index) { var zr = this.zr; var options=this.options; var showCol = options.showCol; var sec=options.wellSec; var checkColor = options.checkColor; var w = this.w; var h = this.h; var disLeft=this.disLeft*w; var disRight=this.disRight*w; var disTop=this.disTop*h; var disBottom=this.disBottom*h; var wRadio=(w-disLeft-disRight)/(this.endSec-this.beginSec); var hRadio=(h-disTop-disBottom)/(this.maxData-this.minData); var barValue=Number(options.data[index][showCol]); var bg = (options.data[index][sec]).split(this.split)[0]; bg = Number(bg); var ed = (options.data[index][sec]).split(this.split)[1]; ed = Number(ed); if (index < this.length) { //恢复移除部分 if (this.preZrEle) { this.preZrEle.attr({ style: { stroke: null, lineWidth:0, fill: this.originLinearColor } }); } if(this.tipLine) { zr.remove(this.tipLine); } if(this.tipText) { zr.remove(this.tipText); } //改变添加部分 var checkZr = this.zrEleArray[index]; if (checkZr) { this.preZrEle = checkZr; //设置元素属性 checkZr.attr({ style: { /* stroke: '#FF5454',*/ lineWidth:4, fill: new zrender.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: checkColor[0] }, { offset: 1, color: checkColor[1] } ]) } }); //提示虚线 var tipLine = new zrender.Line({ shape: { x1:disLeft, y1:h-disBottom-hRadio*(barValue-this.minData), x2:disLeft+wRadio*(ed-this.beginSec), y2:h-disBottom-hRadio*(barValue-this.minData), percent:0 }, style: { stroke:'#434348', lineDash:[5,5] } }); tipLine.animate('shape', false) .when(500, { percent: 1 }).start(); zr.add(tipLine); this.tipLine=tipLine; //提示文字 var tipText = new zrender.Text({ style: { stroke: '#434348', text:barValue, fontSize: '10' }, position: [disLeft+wRadio*(ed-this.beginSec),h-disBo c16f ttom-hRadio*(barValue-this.minData)] }); zr.add(tipText); this.tipText=tipText; } } else { console.log('该索引下没有zrender元素'); } }, clickEle:function(zrEle){ var self=this; zrEle.on('click',function(){ var rowIndex=zrEle.rowIndex; self.activeEle(rowIndex); }); }, //销毁实例 dispose:function(){ var zr = this.zr; zrender.dispose(zr); //移除反向绑定 this.$element.removeData(NAMESPACE); } }; // Register as jQuery plugin $.fn.dirCard = function (options) { var args = toArray(arguments, 1); var result; this.each(function () { //console.log(this); var $this = $(this); var data = $this.data(NAMESPACE); var fn; if (!data) { $this.data(NAMESPACE, (data = new dirCard(this, options))); } if (isString(options) && $.isFunction(fn = data[options])) { result = fn.apply(data, args); } }); return isUndefined(result) ? this : result; }; });
调用示例
<!-- Created by ChenCen on 2017/12/20 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="zrender.js"></script> <script type="text/javascript" src="demo.js"></script> </head> <body> <div class="example-container" style="width:800px;height:400px"></div> <script> str='[{"段":"794-810","value":5},' + '{"段":"815-823","value":5.6},' + '{"段":"840-849","value":3},' + '{"段":"855-860","value":7},' + '{"段":"877-893","value":2.6},' + '{"段":"920-945","value":8.3},' + '{"段":"955-962","value":4.5},' + '{"段":"970-977","value":6},' + '{"段":"980-988","value":7.1},' + '{"段":"993-1007","value":3.3},' + '{"段":"1008-1011","value":8.1},' + '{"段":"1020-1035","value":6.3}]'; jsonData=JSON.parse(str); $('.example-container').dirCard ( { data:jsonData, wellSec:'段', showCol:'value', barColor:['rgba(114, 172, 209, 1)','rgba(114, 172, 209, 0.5)'], backgroundColor:'#ccc' } ); </script> </body> </html>
效果图
动态效果图:
相关文章推荐
- android 自定义view学习笔记————Paint和Canvas的简单使用
- 使用简单的轻量级移动web应用开发框架 - Sidetap
- 安卓开发笔记2--简单的使用按钮
- 使用hessian开发WebService,轻量级,更简单、快捷
- ios开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- 使用简单的轻量级移动web应用开发框架 - Sidetap
- 使用VS2010开发一个简单的自定义字段类型
- iOS开发笔记22--使用XIB自定义一个UIView
- iOS开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- ios开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- SharePoint开发学习笔记4——使用aspx自定义表单的工作流(1)
- ios开发UI基础—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- ios开发UI篇—使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- Flex企业应用开发实践学习笔记(六)——使用ActionScript创建自定义组件
- NodeJS-学习笔记(2)--使用node提供的express框架开发简单的web应用
- iOS开发UI基础—24使用纯代码自定义UItableviewcell实现一个简单的微博界面布局
- iPhone开发学习笔记005——使用XIB自定义一个UIView,然后将这个view添加到controller的view
- iPhone开发学习笔记005——使用XIB自定义一个UIView,然后将这个view添加到controller的view
- iPhone开发学习笔记005——使用XIB自定义一个UIView,然后将这个view添加到controller的view