上一篇说到:ZRender源码分析2:Storage(Model层),这次咱看来看看Painter-View层
Painter这个类主要负责MVC中的V(View)层,负责将Storage中的shape对象绘制到canvas中,包括了:更新、渲染、变化大小、导出、修改等操作。Painter这个类还是很明显的构造函数,然后把方法赋值到Painter.PRototype上,无新奇之处,下面为示例代码。只有在Painter.js末尾有一个内部的createDom函数,很明显,传入id,type(tagName),painter(用来确定宽高)来创建一个新的dom元素,并且这个dom元素的宽高与painter的相同,tagname为type,绝对定位,拥有一个自定义属性key为ata-zr-dom-id,value为id。
function Painter(root,stroage) {this.root = xxxx;}Painter.prototype.render = function () {};Painter.prototype.refresh = function () {};Painter.prototype.update = function () {};Painter.prototype.clear = function () {};..... /** * 创建dom * * @inner * @param {string} id dom id 待用 * @param {string} type dom type,such as canvas, div etc. * @param {Painter} painter painter instance */function createDom(id, type, painter) { var newDom = document.createElement(type); var width = painter._width; var height = painter._height; // 没append呢,请原谅我这样写,清晰~ newDom.style.position = 'absolute'; newDom.style.left = 0; newDom.style.top = 0; newDom.style.width = width + 'px'; newDom.style.height = height + 'px'; newDom.setAttribute('width', width * devicePixelRatio); newDom.setAttribute('height', height * devicePixelRatio); // id不作为索引用,避免可能造成的重名,定义为私有属性 newDom.setAttribute('data-zr-dom-id', id); return newDom;}
/** * 绘图类 (V) * * @param {HTMLElement} root 绘图区域 * @param {storage} storage Storage实例 */function Painter(root, storage) { this.root = root; this.storage = storage; root.innerHTML = ''; this._width = this._getWidth(); // 宽,缓存记录 this._height = this._getHeight(); // 高,缓存记录 var domRoot = document.createElement('div'); this._domRoot = domRoot; //domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬 domRoot.style.position = 'relative'; domRoot.style.overflow = 'hidden'; domRoot.style.width = this._width + 'px'; domRoot.style.height = this._height + 'px'; root.appendChild(domRoot); this._domList = {}; //canvas dom元素 this._ctxList = {}; //canvas 2D context对象,与domList对应 this._domListBack = {}; this._ctxListBack = {}; this._zLevelConfig = {}; // 每个zLevel 的配置,@config clearColor this._maxZlevel = storage.getMaxZlevel(); //最大zlevel,缓存记录 // this._loadingTimer this._loadingEffect = new BaseLoadingEffect({}); this.shapeToImage = this._createShapeToImageProcessor(); // 创建各层canvas // 背景 this._domList.bg = createDom('bg', 'div', this); domRoot.appendChild(this._domList.bg); var canvasElem; var canvasCtx; /** * 每一个level,就是一个canvas * * DOM结构 * root * ->domRoot * ->canvas level1 * ->canvas level2 * ->canvas level3 * ->canvas hover_level * * _domList保存所有的DOM引用 * { * 1:CanvasHTMLElement * 2:CanvasHTMLElement * 3:CanvasHTMLElement * hover:CanvasHTMLElement * } * * ctxList保存所有的canvas.getContext('2d')引用 * { * 1:CanvasContext * 2:CanvasContext * 3:CanvasContext * hover:CanvasContext * } */ // 实体 for (var i = 0; i <= this._maxZlevel; i++) { canvasElem = createDom(i, 'canvas', this); domRoot.appendChild(canvasElem); this._domList[i] = canvasElem; vmlCanvasManager && vmlCanvasManager.initElement(canvasElem); // excanvas method this._ctxList[i] = canvasCtx = canvasElem.getContext('2d'); if (devicePixelRatio != 1) { canvasCtx.scale(devicePixelRatio, devicePixelRatio); } } // 高亮 canvasElem = createDom('hover', 'canvas', this); canvasElem.id = '_zrender_hover_'; domRoot.appendChild(canvasElem); this._domList.hover = canvasElem; vmlCanvasManager && vmlCanvasManager.initElement(canvasElem); // excanvas method this._domList.hover.onselectstart = returnFalse; this._ctxList.hover = canvasCtx = canvasElem.getContext('2d'); if (devicePixelRatio != 1) { //处理视网膜 canvasCtx.scale(devicePixelRatio, devicePixelRatio); }}Painter.prototype._getWidth = function() { var root = this.root; var stl = root.currentStyle || document.defaultView.getComputedStyle(root); return ((root.clientWidth || parseInt(stl.width, 10)) - parseInt(stl.paddingLeft, 10) // 请原谅我这比较粗暴 - parseInt(stl.paddingRight, 10)).toFixed(0) - 0; /** * 这里用实际的width减去了左右的padding * 为什么不考虑将这两个方法就行重载? */};Painter.prototype._getHeight = function () { var root = this.root; var stl = root.currentStyle || document.defaultView.getComputedStyle(root); return ((root.clientHeight || parseInt(stl.height, 10)) - parseInt(stl.paddingTop, 10) // 请原谅我这比较粗暴 - parseInt(stl.paddingBottom, 10)).toFixed(0) - 0;};
//////////////以下为zrender.js中代码/////////////////////** * 将常规shape转成image shape */ZRender.prototype.shapeToImage = function(e, width, height) { var id = guid(); return this.painter.shapeToImage(id, e, width, height);};//////////////以下为Painter.js中代码////////////////////Painter.prototype._createShapeToImageProcessor = function () { if (vmlCanvasManager) { return doNothing; } var painter = this; var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); var devicePixelRatio = window.devicePixelRatio || 1; return function (id, e, width, height) { return painter._shapeToImage( id, e, width, height, canvas, ctx, devicePixelRatio ); };};Painter.prototype._shapeToImage = function ( id, shape, width, height, canvas, ctx, devicePixelRatio) { canvas.style.width = width + 'px'; canvas.style.height = height + 'px'; canvas.setAttribute('width', width * devicePixelRatio); canvas.setAttribute('height', height * devicePixelRatio); ctx.clearRect(0, 0, width * devicePixelRatio, height * devicePixelRatio); var shapeTransform = { position : shape.position, rotation : shape.rotation, scale : shape.scale }; shape.position = [0, 0, 0]; shape.rotation = 0; shape.scale = [1, 1]; if (shape) { shape.brush(ctx, false); } var ImageShape = require( './shape/Image' ); var imgShape = new ImageShape({ id : id, style : { x : 0, y : 0, // TODO 直接使用canvas而不是通过base64 image : canvas.toDataURL() } }); if (shapeTransform.position != null) { imgShape.position = shape.position = shapeTransform.position; } if (shapeTransform.rotation != null) { imgShape.rotation = shape.rotation = shapeTransform.rotation; } if (shapeTransform.scale != null) { imgShape.scale = shape.scale = shapeTransform.scale; } return imgShape;};
新闻热点
疑难解答