入口2: 渲染
// zrender_demo.html zr.render(); // zrender.js /** * 渲染 * * @param {Function} callback 渲染结束后回调函数 * todo:增加缓动函数 */ ZRender.prototype.render = function (callback) { this.painter.render(callback); return this; };
然后我们看看Painter是如何渲染的。(这边的回调是undefined)
1. 先关闭正在显示的数据加载提示
// Painter.js
/** * 首次绘图,创建各种dom和context * * @param {Function=} callback 绘画结束后的回调函数 */ Painter.prototype.render = function (callback) { if (this.isLoading()) { this.hideLoading(); } //...... return this; };
2. 检查_maxZlevel是否变大,如是则同步创建需要的Canvas。这次并不需要更新。
this._syncMaxZlevelCanvase(); /** * 检查_maxZlevel是否变大,如是则同步创建需要的Canvas * * @private */ Painter.prototype._syncMaxZlevelCanvase = function () { var curMaxZlevel = this.storage.getMaxZlevel(); if (this._maxZlevel < curMaxZlevel) { //实体 for (var i = this._maxZlevel + 1; i <= curMaxZlevel; i++) { var canvasElem = createDom(i, 'canvas', this); this._domList[i] = canvasElem; this._domRoot.insertBefore(canvasElem, this._domList.hover); if (vmlCanvasManager) { vmlCanvasManager.initElement(canvasElem); } var canvasCtx = canvasElem.getContext('2d'); this._ctxList[i] = canvasCtx; if (devicePixelRatio != 1) { canvasCtx.scale(devicePixelRatio, devicePixelRatio); } } this._maxZlevel = curMaxZlevel; } };
3. 依然是好习惯,先清空已有内容。这边的clearLayer() API比较复杂,晚点再细究吧。
//清空已有内容,render默认为首次渲染 this.clear(); /** * 清除hover层外所有内容 */ Painter.prototype.clear = function () { for (var k in this._ctxList) { if (k == 'hover') { continue; } this.clearLayer(k); } return this; };
this._ctxList的值如下,这边只需要清空非高亮画布
4. 升序遍历,shape上的zlevel指定绘画图层的z轴层叠
//升序遍历,shape上的zlevel指定绘画图层的z轴层叠 this.storage.iterShape( this._brush({ all : true }), { normal: 'up' } ); /** * 遍历迭代器 * * @param {Function} fun 迭代回调函数,return true终止迭代 * @param {Object=} option 迭代参数,缺省为仅降序遍历常规形状 * hover : true 是否迭代高亮层数据 * normal : 'down' | 'up' | 'free' 是否迭代常规数据,迭代时是否指定及z轴顺序 */ Storage.prototype.iterShape = function (fun, option) { // ..... // 设置option默认值,默认降序遍历 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': // 降序遍历,高层优先 //...... 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; };
还记得之前初始化Painter时提到的_zElements不,当时说的就是根据zlevel来组织画布,这边就用到了。状态如下:
遍历中,会对circle进行刷画图像
function(shape) { if ((changedZlevel.all || changedZlevel[shape.zlevel]) && !shape.invisible ) { var ctx = ctxList[shape.zlevel]; if (ctx) { if (!shape.onbrush //没有onbrush //有onbrush并且调用执行返回false或undefined则继续粉刷 || (shape.onbrush && !shape.onbrush(ctx, false)) ) { if (config.catchBrushException) { try { shape.brush(ctx, false, updatePainter); } catch(error) { log( error, 'brush error of ' + shape.type, shape ); } } else { shape.brush(ctx, false, updatePainter); } } } else { log( 'can not find the specific zlevel canvas!' ); } } };
其中shape.brush()暂时不细究。等下一轮吧。
5. update到最新则清空标志位。Storage中只是清空了_changedZlevel。
// update到最新则清空标志位 this.storage.clearChangedZlevel(); // Storage.js Storage.prototype.clearChangedZlevel = function () { this._changedZlevel = {}; return this; };
6. callback为undefined,所以下面的不执行。
/** * 首次绘图,创建各种dom和context * * @param {Function=} callback 绘画结束后的回调函数 */ Painter.prototype.render = function (callback) { // ....... // 这部分代码刚才都分析过 if (typeof callback == 'function') { callback(); } return this; };
Painter的初步分析到此为止。接下来分析先Handler,再细究Painter中跳过的部分。