标签:style blog http color os io ar for 数据
上一篇请移步:zrender源码分析1:总体结构
本篇进行ZRender的MVC结构中的M进行分析
上篇说到,Storage负责MVC层中的Model,也就是模型,对于zrender来说,这个model就是shape对象,在1.x表现的还不强烈,到了2.x, 在zr.addShape()的时候,传入的参数就必须是new出来的对象了详情请看这里 2.x相比1.x的变化 ,关于这个变化多说点吧,那就是从1.x升级到2.x的时候,因为方式变了,总不能改掉所有的代码,总不能像ext一样, (从ExtJS3升级到ExtJS4是一个特别痛苦的过程),所以我们在原有的可视化程序中,加入了如下helper(该程序基于ExtJS5)
Ext.define(‘Nts.Utils.ChartHelper‘, {
singleton: true,
shapeMap: {},
requireMap: {},
/**
* 通过shape的类型获得shape的构造函数
* 由于zrender的升级,所以导致该方法的出现,详情
* see:https://github.com/ecomfe/zrender/wiki/2.x%E7%9B%B8%E6%AF%941.x%E7%9A%84%E5%8F%98%E5%8C%96
*
* @param shapeType shape类型
* @returns {Constructor}
*/
getShapeTypeConstructor: function (shapeType) {
// 由于zrender2.0的addShape时不能add对象,只能add一个初始化好的shape类,
// 所以每次都需要require加载所需的类,在这里,shapeMap是一个缓存对象
// 因为echarts包含了requirejs的源码,但是没有将define和require方法暴露出来
// 迫不得已修改了echarts的源代码,window.require = require;
if (!this.shapeMap[shapeType]) {
this.shapeMap[shapeType] = require(‘zrender/shape/‘ + Ext.String.capitalize(shapeType));
}
return this.shapeMap[shapeType];
},
/**
* 根据shape类型和传入shape的参数,新建shape类,返回的结果可以直接被addShape
*
* 该方法有多个重载,如下
*
* 1.Nts.Utils.ChartHelper.makeShapeInstance(‘image‘,{scale:[1,2],hover:....});
* 2.Nts.Utils.ChartHelper.makeShapeInstance({shape:‘image‘,scale:[1,2],hover:....});
*
* 第2中方式为zrender1.x中兼容的方式,其中shape属性可以是 shape|shapeType|type
*
* @param shapeType shape类型
* @param option 参数
* @returns {Object} shape对象
*/
makeShapeInstance: function (shapeType, option) {
if (Ext.isObject(shapeType)) {
option = shapeType;
shapeType = option.shape || option.shapeType || option.type
}
var ctor = this.getShapeTypeConstructor(shapeType);
if (!ctor) new Error(‘cannot find this shape in zrender‘);
return new ctor(option);
}
});
这样一来,就能够继续像之前一样愉快的玩耍了。言归正传,把代码全部折叠起来,我们来看看总体的结构。
还好还好,这里的结构还是超级简单。
下面,咱们来逐个击破。
二话不说,先贴代码
/**
* 内容仓库 (M)
*
*/
function Storage() {
// 所有常规形状,id索引的map
this._elements = {};
// 所有形状的z轴方向排列,提高遍历性能,zElements[0]的形状在zElements[1]形状下方
this._zElements = [];
// 高亮层形状,不稳定,动态增删,数组位置也是z轴方向,靠前显示在下方
this._hoverElements = [];
// 最大zlevel
this._maxZlevel = 0;
// 有数据改变的zlevel
this._changedZlevel = {};
}
作者都注释了,这是个内容仓库,又想想,这不就是相当于粮仓嘛,shape对象就是一个一个的粮食。构造函数里的_elements,_zElement,_hoverElements就是粮仓。 而_elements和_zElements这两个变量其实存入的是一样的东西,只是存入的方式不太相同而已。其中,zElement这个变量中的z,大概就是zlevel(分层)的意思, 我想这便是zrender的最核心的思想,分层绘图。接下来咱们用一个取(bei)巧(bi)的方式,来看看内存中的呈现。打开zrender.js,加入一行代码:window.z = this;
function ZRender(id, dom) {
this.id = id;
this.env = require(‘./tool/env‘);
this.storage = new Storage();
this.painter = new Painter(dom, this.storage);
this.handler = new Handler(dom, this.storage, this.painter);
window.z = this; // 把z透漏出去
// 动画控制
this.animatingShapes = [];
this.animation = new Animation({
stage : {
update : getAnimationUpdater(this)
}
});
this.animation.start();
}
然后,运行如下示例:
require([‘../src/zrender‘,
‘../src/shape/Image‘,
‘../src/shape/Text‘,
‘../src/shape/Circle‘],
function (zrender, ImageShape, TextShape, CircleShape) {
var box = document.getElementById(‘box‘);
var zr = zrender.init(box);
zr.addShape(new CircleShape({
style: {
x: 120,
y: 120,
r: 50,
color: ‘red‘
},
hoverable: true
}));
zr.addShape(new TextShape({
style: {
x: 220,
y: 220,
color: ‘red‘,
text: ‘something text‘
},
hoverable: true,
zlevel: 2
}));
zr.render();
});
最后,在控制台中输入z,回车,看到如下打印:
可以很明显的看到,_elements里的东西,是直接塞入的,不管什么顺序,而zElements里的东西,是按照shape对象的zlevel进行存放的,具体怎么维护,就要看怎么增删改查了
PS:这张图比较重要,在下面增删改查的时候,可以详尽的表现出其过程
/**
* 添加
*
* @param {Shape} shape 参数
*/
Storage.prototype.add = function (shape) {
shape.updateNeedTransform();
shape.style.__rect = null;
this._elements[shape.id] = shape;
this._zElements[shape.zlevel] = this._zElements[shape.zlevel] || [];
this._zElements[shape.zlevel].push(shape);
this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
this._changedZlevel[shape.zlevel] = true;
/**
* _elements ->
* {
* _zrender_101_: shapeObject,
* _zrender_102_: shapeObject,
* _zrender_103_: shapeObject,
* ...
* }
*
* _zrender_103_ 为guid生成的
*
* _zElements ->
* {
* 1: [shapeObject,shapeObject],
* 2: [shapeObject,shapeObject....],
* 3. [...]
* }
*
* 123 为层数
*
* _maxZlevel: 3
* changedZlevel: {1:true,2:true....}
*/
return this;
};
/**
* 添加高亮层数据
*
* @param {Object} params 参数
*/
Storage.prototype.addHover = function (params) {
/**
* 这里判断了一大推参数,来预处理是否需要变形,变形金刚(Transformers)
* 豆瓣电影:http://movie.douban.com/subject/7054604/
* 在最初添加的时候,处理变形开关,就不用在用到的时候重新做了
*/
if ((params.rotation && Math.abs(params.rotation[0]) > 0.0001)
|| (params.position
&& (Math.abs(params.position[0]) > 0.0001
|| Math.abs(params.position[1]) > 0.0001))
|| (params.scale
&& (Math.abs(params.scale[0] - 1) > 0.0001
|| Math.abs(params.scale[1] - 1) > 0.0001))
) {
params.needTransform = true;
}
else {
params.needTransform = false;
}
this._hoverElements.push(params); //简单的将高亮层push到_hoverElements中
return this;
};
/**
* 删除高亮层数据
*/
Storage.prototype.delHover = function () {
this._hoverElements = [];
return this;
};
/**
* 删除,shapeId不指定则全清空
*
* @param {string= | Array} idx 唯一标识
*/
Storage.prototype.del = function (shapeId) {
if (typeof shapeId != ‘undefined‘) {
var delMap = {};
/**
* 处理各种重载
* 1.如果不是个数组,直接加入到delMap中
* 2.如果是个数组,遍历之
*/
if (!(shapeId instanceof Array)) {
// 单个
delMap[shapeId] = true;
}
else {
// 批量删除
if (shapeId.lenth < 1) { // 空数组
return;
}
for (var i = 0, l = shapeId.length; i < l; i++) {
delMap[shapeId[i].id] = true;
}
}
var newList;
var oldList;
var zlevel;
var zChanged = {};
for (var sId in delMap) {
if (this._elements[sId]) {
zlevel = this._elements[sId].zlevel;
this._changedZlevel[zlevel] = true;
/**
* 这里主要处理zElements中元素的删除
* 这里确认每一个zlevel只遍历一次,因为一旦进入这个if,在if的末尾,就会将flag设置为false,下次就进不来
*
* 1.遍历delMap,取出单个shape的zlevel,然后从_zElements[zlevel] 取出所有,命名为oldList
* 2.遍历oldList,如果delMap中没有当前遍历的shape,就加入到newList,最后该层的_zElements[zlevel]就是newList
* 3.设置标志位,使之为false,表示该层已经被处理,就不要再次处理了
*/
if (!zChanged[zlevel]) {
oldList = this._zElements[zlevel];
newList = [];
for (var i = 0, l = oldList.length; i < l; i++){
if (!delMap[oldList[i].id]) {
newList.push(oldList[i]);
}
}
this._zElements[zlevel] = newList;
zChanged[zlevel] = true;
}
//将shape从_elements中删除
delete this._elements[sId];
}
}
}
else{
// 不指定shapeId清空
this._elements = {};
this._zElements = [];
this._hoverElements = [];
this._maxZlevel = 0; //最大zlevel
this._changedZlevel = { //有数据改变的zlevel
all : true
};
}
return this;
};
/**
* 修改
*
* @param {string} idx 唯一标识
* @param {Object} params 参数
*/
Storage.prototype.mod = function (shapeId, params) {
var shape = this._elements[shapeId];
if (shape) {
shape.updateNeedTransform();
shape.style.__rect = null;
this._changedZlevel[shape.zlevel] = true; // 可能修改前后不在一层
/**
* 将参数合并,params && util.merge(shape, params, true);
*
* this._changedZlevel[shape.zlevel] = true; 这里是为了防范:
*
* var imageShape = new ImageShape({src:‘xxx.png‘,zlevel:1});
* imageShape.mod({zlevel:3});
*
* 这里就是:level1和level3都变化了,_maxZlevel也变化了。
*/
if (params) {
util.merge(shape, params, true);
}
this._changedZlevel[shape.zlevel] = true; // 可能修改前后不在一层
this._maxZlevel = Math.max(this._maxZlevel, shape.zlevel);
}
return this;
};
/**
* 遍历迭代器
*
* @param {Function} fun 迭代回调函数,return true终止迭代
* @param {Object=} option 迭代参数,缺省为仅降序遍历常规形状
* hover : true 是否迭代高亮层数据
* normal : ‘down‘ | ‘up‘ | ‘free‘ 是否迭代常规数据,迭代时是否指定及z轴顺序
*/
Storage.prototype.iterShape = function (fun, option) {
/**
* 处理默认情况 option = option ||{ hover: false, normal: ‘down‘};
*/
if (!option) {
option = {
hover: false, //不遍历高亮层
normal: ‘down‘ //高层优先
};
}
if (option.hover) {
//高亮层数据遍历
for (var i = 0, l = this._hoverElements.length; i < l; i++) {
if (fun(this._hoverElements[i])) {
return this;
}
}
}
var zlist;
var len;
if (typeof option.normal != ‘undefined‘) {
//z轴遍历: ‘down‘ | ‘up‘ | ‘free‘
switch (option.normal) {
case ‘down‘:
// 降序遍历,高层优先
var l = this._zElements.length;
while (l--) {
zlist = this._zElements[l];
if (zlist) {
len = zlist.length;
while (len--) {
if (fun(zlist[len])) {
return this;
}
}
}
}
break;
case ‘up‘:
//升序遍历,底层优先
for (var i = 0, l = this._zElements.length; i < l; i++) {
zlist = this._zElements[i];
if (zlist) {
len = zlist.length;
for (var k = 0; k < len; k++) {
if (fun(zlist[k])) {
return this;
}
}
}
}
break;
// case ‘free‘:
default:
//无序遍历
for (var i in this._elements) {
if (fun(this._elements[i])) {
return this;
}
}
break;
}
}
return this;
};
/**
* 根据指定的shapeId获取相应的shape属性
*
* @param {string=} idx 唯一标识
*/
Storage.prototype.get = function (shapeId) {
return this._elements[shapeId];
};
Storage.prototype.getMaxZlevel = function () {
return this._maxZlevel;
};
Storage.prototype.getChangedZlevel = function () {
return this._changedZlevel;
};
Storage.prototype.clearChangedZlevel = function () {
this._changedZlevel = {};
return this;
};
Storage.prototype.setChangedZlevle = function (level) {
this._changedZlevel[level] = true;
return this;
};
Storage.prototype.hasHoverShape = function () {
return this._hoverElements.length > 0;
};
标签:style blog http color os io ar for 数据
原文地址:http://www.cnblogs.com/hhstuhacker/p/zrender-source-storage-advance.html