轻量级的Canvas类库zrender使用笔记 :简单自定义图件开发

来源:互联网 发布:淘宝手工食品放哪类目 编辑:程序博客网 时间:2024/06/05 14:20
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.lineDashnumber[]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)

绑定事件处理。

参数

名称类型默认值描述eventNamestring 事件名称,支持: 'click'、 'mousedown'、 'mouseup'、 'mousewheel'、 'dblclick'、 'contextmenu'eventHandlerFunction 事件处理的回调函数。contextObject 函数上下文。


下面写了一个简单的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-disBottom-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>


效果图



动态效果图: