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

article/2025/9/21 5:02:37

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

https://github.com/zrysmt/echarts3/tree/master/zrender

基于版本 3.2.2。

1.总体架构

官网上的一张图和解释。

MVC结构分别在Stroage.js,Painter.js,Handler.js文件下,我们稍后会详细解释,现在我们大概来看下它们分别的作用。
* Stroage(M) : shape数据CURD管理
* Painter(V) : canvase元素生命周期管理,视图渲染,绘画,更新控制
* Handler(C) : 事件交互处理,实现完整dom事件模拟封装
* shape : 图形实体,分而治之的图形策略,可定义扩展
* tool : 绘画扩展相关实用方法,工具及脚手架
* animation : 动画扩展,提供promise式的动画接口和常用缓动函数

源码结构

目录的介绍
- animation 动画有关;
- contain 包含判断;
- container Group.js 元素组的概念;
- core 核心代码,包含一些工具(util.js)、事件(event.js)、唯一ID(guid.js)、矩阵运算有关(matrix.js)等;
- dom HandleProxy.js dom事件有关;
- graphic 图形有关,shape文件夹下就是各个图形的js文件;
- mixin 混入模式要混入的函数;
- tool 工具函数,包括颜色工具(color.js),path工具(path.js)和转换工具(transformPath.js);
- vml IE中的画笔,[vml解释进入](http://www.g168.net/txt/vml/]
- 全局的文件
+ config.js 配置文件
+ Element.js 元素文件作为zrender最基本的元素
+ Handle.js C层,控制层
+ Layer.js 图层管理
+ Painter.js V层,视图层
+ Storage.js M层,数据管理层
+ zrender.js 入口

2.入口(zrender.js)

2.1 初始化

类似于jquery的无new化处理,init调用即可
调用:

var zr = zrender.init(document.getElementById('main'));

源码:

    var instances = {};    // ZRender实例map索引var zrender = {};zrender.init = function(dom, opts) {var zr = new ZRender(guid(), dom, opts);instances[zr.id] = zr;return zr;};

2.2 构造函数

我们可以在构造函数中,看到MVC的管理机制。

var ZRender = function(id, dom, opts) {opts = opts || {};this.dom = dom;this.id = id;var self = this;var storage = new Storage();var rendererType = opts.renderer;if (useVML) {//IE中使用VML渲染if (!painterCtors.vml) {throw new Error('You need to require \'zrender/vml/vml\' to support IE8');}rendererType = 'vml';} else if (!rendererType || !painterCtors[rendererType]) {rendererType = 'canvas';}var painter = new painterCtors[rendererType](dom, storage, opts);this.storage = storage;//Mthis.painter = painter;//Vvar handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null;this.handler = new Handler(storage, painter, handerProxy, painter.root);//Cconsole.log(this);//这里是我增加的为了调试使用的/*** @type {module:zrender/animation/Animation}* 动画控制*/this.animation = new Animation({stage: {update: zrUtil.bind(this.flush, this)}});this.animation.start();this._needsRefresh;// 修改 storage.delFromMap, 每次删除元素之前删除动画var oldDelFromMap = storage.delFromMap;var oldAddToMap = storage.addToMap;storage.delFromMap = function(elId) {var el = storage.get(elId);oldDelFromMap.call(storage, elId);el && el.removeSelfFromZr(self);};storage.addToMap = function(el) {oldAddToMap.call(storage, el);el.addSelfToZr(self);};
};

2.3 ZRender.prototype

具体的方法及其注释可以在我的github中查看,这里只将方法名放在这里。

ZRender.prototype = {constructor: ZRender,/*** 获取实例唯一标识* @return {string}*/getId: function () {},/*** 添加元素后就会渲染* @param  {module:zrender/Element} el*/add: function (el) {this.storage.addRoot(el);this._needsRefresh = true;},/*** 删除元素* @param  {module:zrender/Element} el*/remove: function (el) { },configLayer: function (zLevel, config) {},/** Repaint the canvas immediately*/refreshImmediately: function () {},/** Mark and repaint the canvas in the next frame of browser*/refresh: function() {},flush: function () {},/**Add element to hover layer */addHover: function (el, style) {},/** Add element from hover layer* @param  {module:zrender/Element} el*/removeHover: function (el) {},/** Clear all hover elements in hover layer*/clearHover: function () {},/** Refresh hover in next frame*/refreshHover: function () {},/**Refresh hover immediately*/refreshHoverImmediately: function () {     ;},resize: function(opts) {},clearAnimation: function () {},/** Get container width */getWidth: function() {},getHeight: function() {},/** Converting a path to image */pathToImage: function(e, width, height) {},/*** Set default cursor* @param {string} [cursorStyle='default'] 例如 crosshair*/setCursorStyle: function (cursorStyle) {},/**发布订阅模式 */on: function(eventName, eventHandler, context) {},off: function(eventName, eventHandler) {},trigger: function (eventName, event) {},/** Clear all objects and the canvas */clear: function () {},/** Dispose self */dispose: function () {}};

源码的方法,我们以add举例子,它其实调用的是this.storage.addRoot方法,使用MVC机制处理。
使用示例:

var circle1 = new Circle({shape: {cx: 100,cy: 100,r: 30},style: {fill: 'blue'},draggable: true
});
zr.add(circle1);
circle1.on('mouseover', function() {zr.addHover(this, {stroke: 'yellow',lineWidth: 10,opacity: 1});zr.refresh();
});
circle1.on('mouseout', function() {zr.removeHover(this);
});

注意:这里有addHover方法,所以会渲染两个canvas。如果没有addHover,就只会渲染一个canvas。

3.MVC简单概述

MVC对应三个文件的结构很简单,其实就是一个构造函数,一个prototype原型扩展。

3.1 M–数据管理层(Storage.js)

我们看构造函数,将元素存储在this._elements(对象)、this._roots(数组)和this._displayList(数组)中,然后负责在其中进行增(addRoot,addToMap)删(delRoot,delFromMap)改(updateDisplayList)查(get,getDisplayList)。

   var Storage = function () {// 所有常规形状,id索引的mapthis._elements = {};//和this._elements存放的元素一样,只不过是数组this._roots = [];//和this.roots一样this._displayList = [];//this._displayList的长度this._displayListLen = 0;};

3.2 C–控制层(Handle.js)

Handler负责事件处理,包括’click’, ‘dblclick’, ‘mousewheel’, ‘mouseout’, ‘mouseup’, ‘mousedown’, ‘mousemove’, ‘contextmenu’等。我们知道canvas API没有提供监听每个元素的机制,这就需要一些处理。处理的思路是:监听事件的作用坐标(如点击时候的坐标),判断在哪个绘制元素的范围中,如果在某个元素中,这个元素就监听该事件。具体的思路可以查看参考阅读给的链接文章。

    Handler.prototype = {mousemove:function (event){}//... ...}util.mixin(Handler, Eventful);//混入,下面我们会解释到util.mixin(Handler, Draggable);

3.3 V–视图层(Painter.js)

Painter负责真正的绘图操作,这里是比较繁重的部分
* 1.负责canvas及其周边DOM元素的创建与处理
* 2.负责调用各个Shape(预定义好的)进行绘制
* 3.提供基本的操作方法,渲染(render)、刷新(refresh)、尺寸变化(resize)、擦除(clear)等

Painter是调用canvas API实现的绘制,包括颜色,渐变色,变换,矩阵变化,绘制图片、文本等。IE8使用excanvas兼容。

4.设计模式总结

设计模式的总结,我在一篇博客中有写,要想看这方面的知识,可以在这里看。

4.1 AMD模式

AMD即是“异步模块定义”的意思,所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。源码的结构是这样的
- 定义

define(function(require) {return ZRender;
}
  • 使用
require([module], callback);

我们的Demo使用的是百度封装好的AMD模式esl.js(或者使用requirejs也可以),引入方式和使用示例如下:

<script src="../libs/esl.js"></script>
require(['zrender', 'zrender/graphic/shape/Circle', 'zrender/graphic/shape/Polygon'],
function(zrender, Circle, Polygon) { //... ...
});

4.2 继承

在core->util.js,主要的思想就是将子类的prototype指向父类的prototype;子类的构造函数之指向自己。

   function inherits(clazz, baseClazz) {var clazzPrototype = clazz.prototype;function F() {}F.prototype = baseClazz.prototype;clazz.prototype = new F();for (var prop in clazzPrototype) {//属性也继承了clazz.prototype[prop] = clazzPrototype[prop];}clazz.prototype.constructor = clazz;clazz.superClass = baseClazz;//superClass是个自己定义的属性}

另外不要忘了,在构造函数中应该重写父类的属性。

function Displayable(opts) {Element.call(this, opts);
}

调用

zrUtil.inherits(Displayable, Element);

4.3 混入模式

简而言之,混入就是将一个对象的方法复制给另外一个对象。实现在util.js中

function mixin(target, source, overlay) {target = 'prototype' in target ? target.prototype : target;source = 'prototype' in source ? source.prototype : source;defaults(target, source, overlay);
}
function defaults(target, source, overlay) {for (var key in source) {if (source.hasOwnProperty(key) && (overlay ? source[key] != null : target[key] == null)) {target[key] = source[key];}}return target;
}

调用

zrUtil.mixin(Displayable, RectText);

4.4 jquery的extend模式

实现很简单,类似混入模式,将source对象的方法复制给target对象。

function extend(target, source) {for (var key in source) {if (source.hasOwnProperty(key)) {target[key] = source[key];}}return target;}

4.5 发布订阅模式

逻辑在mixin文件夹中的Eventful.js,为Handle(handle.js)混入方法

util.mixin(Handler, Eventful);

包括一下几种方法
- one一次绑定事件
- on 绑定事件
- isSilent是否绑定了事件
- off解绑事件
- trigger事件分发,触发事件
- triggerWithContext带有context的事件分发

5.逻辑关系

  • 步进关系
    –>为扩展或混入,==>为继承自父类,()内部为所在位置, [ ]为扩展或者混入的方式。

Element[Animatable Transformable Eventful] (Element.js) ==>
Displayable[ReactText] (Displayable.js) ==>
Path[Sub] (Path.js) ==>
Sub(Path.js) –>
各类型的shape

底层对象是封装过的Element。

  • 绘制的逻辑

add(zrender.js)–>addRoot(Storage.js) –> addToMap(Storage.js) –>
dirty[标记为脏的,下一帧渲染] (path.js) –> refresh(Painter.js)–>_paintList[遍历_displayList] (Painter.js)–>
_doPaintEl[渲染单个元素] Painter.js) –>brush(Path.js)–>buildPath (各个类型的shape)

参考阅读:
- zrender官方网站
- vml解释
- ZRender源码分析系列
- ZRender源码分析系列源码注释
- HTML5 Canvas绘制的图形的事件处理


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

相关文章

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;行列式 的结果是关…

网页游戏脱机脚本制作视频教程

网页游戏脱机脚本制作视频教程 百度网盘 布脖练馅辰杖怖铱试疤促钩咏合躺酱澈纸罢旨谖谘帘婪尾拾碧鸥居丶骨碳捍饰炔幌干衫乖商衣临衣氛捍运阂妊痰煤籽媒移惶心谑源谑丫松橙湛叭坪佳蛊八婪毒鄙刮碧悠凳炔捍灰钩贺篮媒梅敝粱寡油倨制柿囊谐举婪贫婪奖堂虏啄腊谘剿镜感诺衣挥堂猎…

python可以制作游戏脚本吗_用Python写一个游戏脚本,你会吗?

学习python有一段时间了,由于python语言的强大和简洁,是一个不错的脚本语言,就准备做个游戏脚本练练手。如果你也想多练项目实战。可以去小编的Python交流.裙 :一久武其而而流一思(数字的谐音)转换下可以找到了,里面有最新Python教程项目 听说pywin32写脚本还不错 pyw…

逆水寒商业脚本制作视频

​一章 易语言基础 共6课时 1、关于易语言必须了解的基本知识 2、易语言基本组件(不包括超级列表框)讲解 3、易语言超级列表框详解 3、易语言核心支持库讲解之一 4、易语言核心支持库讲解之二 5、易语言模块制作和DLL制作 6、用制作的模块和DLL开发三个小软件 第二章…

Java开发游戏脚本(第三卷)

游戏脚本开发第三卷 XML文件存储数据使用exe4j打包成exe文件回首BUG最后结语 XML文件存储数据 我举个例子&#xff0c;我的窗口数据需要存储到文件&#xff0c;它的结构为: public class Game {// Game类的成员变量private String Title;private int X;private int Y;private…

乐玩模块脚本实战教程辅助脚本制作开发视频

乐玩插件模块的制作&#xff0c;封装了后台绑定判断&#xff0c;键鼠图色窗口文本输入等游戏辅助常用的方法&#xff0c;每种方法都做了游戏调用测试示范&#xff0c;最后录制了四种多线程方法调用乐玩插件&#xff0c;并通过游戏进行了演示。 学习地址&#xff1a;链接&#x…