ECharts 3.0底层zrender 3.x源码分析1-总体架构
2017-01-11 10:41
639 查看
zrender是一个轻量级的Canvas类库,作为百度Echarts 3.0的底层基础。截至目前查看的zrender源码和文档,包括官网文档都还停留在2.x时代,我打算用一个系列介绍下zrender 3.x的使用和源码,一些demo和没有在博客中介绍的源码请进我的github仓库。
https://github.com/zrysmt/echarts3/tree/master/zrender
基于版本 3.2.2。
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 入口
调用:
源码:
源码的方法,我们以
使用示例:
注意:这里有
* 1.负责canvas及其周边DOM元素的创建与处理
* 2.负责调用各个Shape(预定义好的)进行绘制
* 3.提供基本的操作方法,渲染(render)、刷新(refresh)、尺寸变化(resize)、擦除(clear)等
Painter是调用canvas API实现的绘制,包括颜色,渐变色,变换,矩阵变化,绘制图片、文本等。IE8使用excanvas兼容。
- 定义
使用
我们的Demo使用的是百度封装好的AMD模式esl.js(或者使用requirejs也可以),引入方式和使用示例如下:
另外不要忘了,在构造函数中应该重写父类的属性。
调用
调用
包括一下几种方法
-
-
-
-
-
-
–>为扩展或混入,==>为继承自父类,()内部为所在位置, [ ]为扩展或者混入的方式。
底层对象是封装过的Element。
绘制的逻辑
参考阅读:
- zrender官方网站
- vml解释
- ZRender源码分析系列
- ZRender源码分析系列源码注释
- HTML5 Canvas绘制的图形的事件处理
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;//M this.painter = painter;//V var handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null; this.handler = new Handler(storage, painter, handerProxy, painter.root);//C console.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索引的map this._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绘制的图形的事件处理
相关文章推荐
- ECharts 3.0底层zrender 3.x源码分析3-Handler(C层)
- ECharts 3.0底层zrender 3.x源码分析2-Painter(V层)
- ECharts 3.0源码简要分析1-总体架构
- jQuery源码分析系列:总体架构
- Redis 3.0 源码解析---底层数据结构分析(1)
- 源码阅读分析 - Window底层原理与系统架构
- Hessian源码分析--总体架构
- jQuery源码分析-01总体架构
- Hessian源码分析--总体架构
- jquery源码分析之总体架构
- jQuery源码分析-01总体架构
- Hessian源码分析--总体架构
- Redis 3.0 源码解析---底层数据结构分析(3)
- netty源码分析(十八)Netty底层架构系统总结与应用实践
- jQuery源码分析-01总体架构分析
- jQuery源码分析-01总体架构
- Tomcat源码分析--总体架构
- jQuery源码分析-01总体架构
- Java底层架构之RPC框架Dubbo核心原理之源码分析
- Redis 3.0 源码解析---底层数据结构分析(4)