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

article/2025/9/21 3:18: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);// Constantsvar 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 IE8args.push(offset);}return args.slice.apply(obj, args);}//globlevar 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 drawif (options.rowAfter) {options.rowAfter();}}else {console.log('there must be something wrong!');}},//draw backgrounddrawBG: 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);//axisvar 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 eledrawEle: 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 eventself.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 eleactiveEle: 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>

 

效果图

 

以上只是一个简单的示例,可以尝试用zrender开发功能更全面的自定义图件。

修改后的demo:https://ccessl.github.io/zrender-use-chart/

 


http://chatgpt.dhexx.cn/article/AZRU1t8h.shtml

相关文章

vue-echarts的ZRender事件

首先贴出 vue-echarts 的官网连接&#xff1a;https://www.npmjs.com/package/vue-echarts 1、vue-echarts 具体在vue中的使用方法&#xff0c;本文中就暂时不多介绍了&#xff1b; 2、如何在vue中使用vue-echarts的鼠标事件&#xff1a; 具体使用方式跟vue中使用echarts一样&a…

ZRender文档研读 (基于4.3.2版本)

ZRender文档研读 &#xff08;基于4.3.2版本&#xff09; 不使用最新的5.x.x的版本是因为线上文档和最新版本JS文件不匹配-2022年6月13日 1、文档地址 1、官方文档的地址&#xff1a;https://ecomfe.github.io/zrender-doc/public/api.html#zrenderdisplayable 2、Github地址…

【ZRender 渲染引擎 - 贰】 | Vue 框架集成与绘制其他图元

theme: cyanosis 持续创作&#xff0c;加速成长&#xff01;这是我参与「掘金日新计划 10 月更文挑战」的第 8 天&#xff0c;点击查看活动详情 1. Vue 中使用 ZRender 上一篇中&#xff0c;我们通过最原始的方式体验了一下 ZRender 的使用。接下来&#xff0c;为了更方便管理…

zrender实现图形缩放功能

zrender实现图形缩放功能 一、绘制一个矩形图 <div id"main-container"></div>#main-container {width: 500px;height: 300px;border: 1px solid #888; }import * as zrender from zrenderconst container document.querySelector(#main-container) c…

ECharts 3.0底层zrender 3.x源码分析1-总体架构

zrender是一个轻量级的Canvas类库&#xff0c;作为百度Echarts 3.0的底层基础。截至目前查看的zrender源码和文档&#xff0c;包括官网文档都还停留在2.x时代&#xff0c;我打算用一个系列介绍下zrender 3.x的使用和源码&#xff0c;一些demo和没有在博客中介绍的源码请进我的g…

ZRender (Canvas)简单使用(拖拽、缩放、旋转、文字、层级)

一、ZRender 是二维绘图引擎&#xff0c;它提供 Canvas、SVG、VML 等多种渲染方式。ZRender 也是 ECharts 的渲染器&#xff1b; 二、下面是以图片做的简单demo&#xff0c;分为左中右三部分&#xff0c;左边是需要的图片&#xff0c;中间是绘图部分&#xff0c;右边是添加文字…

源码解读之zrender-ZRender 类(3)

00 小结 当我们在 zrender.init(document.getElementById(“canvas”))时&#xff0c;首先实例化了一个 ZRender 实例&#xff0c;在这个实例化过程中&#xff0c;主要实例化了&#xff1a; Storage 类&#xff0c;作用类似于全局状态管理Painter 类&#xff0c;可以理解为画…

【ZRender 渲染引擎 - 壹】 | 基础图形元素绘制

theme: cyanosis 持续创作&#xff0c;加速成长&#xff01;这是我参与「掘金日新计划 10 月更文挑战」的第 7 天&#xff0c;点击查看活动详情 开篇前言 在掘金认识我的都知道&#xff0c;我主要是研究 Flutter 的。其实我一直希望开发出一套好用的 Flutter 的图表库&#xf…

zrender学习

这个是项目总结&#xff0c;不适合学习 <div id"canvas" style"background-image:url(./canvasbg.gif)"></div> 定义zrender初始化对象&#xff0c;背景是一个gif图 样式如下 ↓ 工程里&#xff0c; topo.html <div id "containe…

zrender源码学习笔记(一):认识zrender

本文内容 入门zrender绘制原理 入门zrender zrender是Echarts底层的2D绘图引擎&#xff0c;在搞懂其原理之前&#xff0c;我们先学会如何使用zrender&#xff0c;我们从绘制一个简单圆形入门。这里也给出官网入门教程 初始化 zrender.init(dom)初始化zrender实例&#xff0c…

zrender 知识:使用zrender搭建流程图工具

首先看下最终的效果图&#xff1a; 主要使用的技术是zrender.js和vue.js&#xff0c;zrender 用于实现流程图&#xff0c;vue搭建整体架构。 本篇文章主要面向对zrender有一定了解的同学。 本篇文章只讲解核心flowchart的实现方法。 一.分析 流程图主要包含节点node、联系e…

zrender基础入门,简单的案例图形绘制

一、简单介绍 ZRender是二维绘图引擎&#xff0c;它提供 Canvas、SVG、VML 等多种渲染方式。ZRender也是ECharts的渲染器。 流程图&#xff1a; 二、使用入口 (1)npm install zrender&#xff0c;因为zrender不是浏览器自带不同于前面的canvas与svg&#xff0c;需要先下载 …

二维绘图引擎ZRender

1、开始使用 描述 ZRender是二维绘图引擎&#xff0c;它提供 Canvas、SVG、VML 等多种渲染方式。ZRender 也是 ECharts 的渲染器。 下载 ZRender 项目在 gitHub ,也可以使用 npm install zrender 下载。 在 dist 目录下找到 zrender.js 和 zrender.min.js&#xff0c;前者是开发…

MATLAB中求矩阵的特征值和特征向量

矩阵特征值的数学定义 设A是n阶方阵&#xff0c;如果存在常数λ和n维非零列向量x&#xff0c;使得等式Axλ x成立&#xff0c;则称λ为A的特征值&#xff0c;x是对应特征值λ的特征向量。 求特征值和特征向量&#xff1a; eig(A)&#xff1a;求矩阵A的全部特征值&#xff0c…

简单易懂的特征值与特征向量

特征值与特征向量是线性代数中一个很基础的知识&#xff0c;但是很多人对这两个概念没有一个直观的概念&#xff0c;从直觉上&#xff0c;很难理解这两个东西&#xff0c;只知道公式&#xff0c;但是不知道它代表的意义。当年上现代课的时候&#xff0c;老师根本不会去讲这些东…

特征值和特征向量的几何意义

1. 特征值和特征向量 我们首先回顾下特征值和特征向量的定义如下&#xff1a; A x λ x Ax\lambda x Axλx 其中A是一个 n n n\times n nn的实对称矩阵&#xff0c; x x x是一个n维向量&#xff0c;则我们说 λ \lambda λ是矩阵A的一个特征值&#xff0c;而 x x x是矩阵A的…

特征值和特征向量的通俗解释

我们知道&#xff0c;特征向量的公式是 ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 其中A代表矩阵&#xff0c;x代表特征向量&#xff0c;代表特征值。 众所周知&#xff0c;特…

对特征值和特征向量的理解

Agenda 1. 特征值和特征向量1.1 特征值和特征向量的通俗解释1.2 如何计算矩阵的特征值和特征向量1.3 特征多项式1.4 特征值和特征向量的性质 1. 特征值和特征向量 在讨论特征值和特征向量之前&#xff0c;必须声明的是现在我们关注的是有限维 线性空间上的线性变换。这里两个关…

线性代数(五)特征值和特征向量

文章目录 一&#xff1a;特征值与特征向量二&#xff1a;特征方程2.1行列式求解的另一种方法--初等变换2.2可逆矩阵定理以及行列式性质的补充2.3特征方程/特征多项式2.4相似性 三&#xff1a;对角化3.1从例子出发3.2定理3.3例子 一&#xff1a;特征值与特征向量 1.定义&#x…

特征值和特征向量(三)

特征值和特征向量&#xff08;三&#xff09; 一、先看一下教科书上的定义&#xff1a;设A是n阶方阵&#xff0c;如果存在常数及非零n向量x&#xff0c;使得&#xff0c;则称是矩阵A的特征值&#xff0c;x是A属于特征值的特征向量。给定n阶矩阵A&#xff0c;行列式 的结果是关…