您的位置:首页 > 其它

轻量级的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、添加矩形,注意默认的填充颜色是黑色

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
5、矩形添加添加鼠标事件

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,使用zrender开发的简单图件。同时将它封装成一个jQuery的插件,方便调用。按照不同的段绘制渐变色的柱状图,同时点击的时候能够动画显示数值。把它命名为demo.js。

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>


效果图



动态效果图:




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐