| Current Path : /home/cbequiperp/www/components/com_flexicontent/librairies/zrender/ |
| Current File : /home/cbequiperp/www/components/com_flexicontent/librairies/zrender/Painter.js |
/**
* Painter绘图模块
* @module zrender/Painter
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
* errorrik (errorrik@gmail.com)
* pissang (https://www.github.com/pissang)
*/
define(
function (require) {
'use strict';
var config = require('./config');
var util = require('./tool/util');
// var vec2 = require('./tool/vector');
var log = require('./tool/log');
// var matrix = require('./tool/matrix');
var BaseLoadingEffect = require('./loadingEffect/Base');
var Layer = require('./Layer');
// 返回false的方法,用于避免页面被选中
function returnFalse() {
return false;
}
// 什么都不干的空方法
function doNothing() {}
function isLayerValid(layer) {
if (!layer) {
return false;
}
if (layer.isBuildin) {
return true;
}
if (typeof(layer.resize) !== 'function'
|| typeof(layer.refresh) !== 'function'
) {
return false;
}
return true;
}
/**
* @alias module:zrender/Painter
* @constructor
* @param {HTMLElement} root 绘图容器
* @param {module:zrender/Storage} storage
*/
var Painter = function (root, storage) {
/**
* 绘图容器
* @type {HTMLElement}
*/
this.root = root;
root.style['-webkit-tap-highlight-color'] = 'transparent';
root.style['-webkit-user-select'] = 'none';
root.style['user-select'] = 'none';
root.style['-webkit-touch-callout'] = 'none';
/**
* @type {module:zrender/Storage}
*/
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._layers = {};
this._zlevelList = [];
this._layerConfig = {};
this._loadingEffect = new BaseLoadingEffect({});
this.shapeToImage = this._createShapeToImageProcessor();
// 创建各层canvas
// 背景
this._bgDom = document.createElement('div');
this._bgDom.style.cssText = [
'position:absolute;left:0px;top:0px;width:',
this._width, 'px;height:', this._height + 'px;',
'-webkit-user-select:none;user-select;none;',
'-webkit-touch-callout:none;'
].join('');
this._bgDom.setAttribute('data-zr-dom-id', 'bg');
domRoot.appendChild(this._bgDom);
this._bgDom.onselectstart = returnFalse;
// 高亮
var hoverLayer = new Layer('_zrender_hover_', this);
this._layers['hover'] = hoverLayer;
domRoot.appendChild(hoverLayer.dom);
hoverLayer.initContext();
hoverLayer.dom.onselectstart = returnFalse;
hoverLayer.dom.style['-webkit-user-select'] = 'none';
hoverLayer.dom.style['user-select'] = 'none';
hoverLayer.dom.style['-webkit-touch-callout'] = 'none';
// Will be injected by zrender instance
this.refreshNextFrame = null;
};
/**
* 首次绘图,创建各种dom和context
*
* @param {Function} callback 绘画结束后的回调函数
*/
Painter.prototype.render = function (callback) {
if (this.isLoading()) {
this.hideLoading();
}
// TODO
this.refresh(callback, true);
return this;
};
/**
* 刷新
* @param {Function} callback 刷新结束后的回调函数
* @param {boolean} paintAll 强制绘制所有shape
*/
Painter.prototype.refresh = function (callback, paintAll) {
var list = this.storage.getShapeList(true);
this._paintList(list, paintAll);
// Paint custum layers
for (var i = 0; i < this._zlevelList.length; i++) {
var z = this._zlevelList[i];
var layer = this._layers[z];
if (! layer.isBuildin && layer.refresh) {
layer.refresh();
}
}
if (typeof callback == 'function') {
callback();
}
return this;
};
Painter.prototype._preProcessLayer = function (layer) {
layer.unusedCount++;
layer.updateTransform();
};
Painter.prototype._postProcessLayer = function (layer) {
layer.dirty = false;
// 删除过期的层
// PENDING
// if (layer.unusedCount >= 500) {
// this.delLayer(z);
// }
if (layer.unusedCount == 1) {
layer.clear();
}
};
Painter.prototype._paintList = function (list, paintAll) {
if (typeof(paintAll) == 'undefined') {
paintAll = false;
}
this._updateLayerStatus(list);
var currentLayer;
var currentZLevel;
var ctx;
this.eachBuildinLayer(this._preProcessLayer);
// var invTransform = [];
for (var i = 0, l = list.length; i < l; i++) {
var shape = list[i];
// Change draw layer
if (currentZLevel !== shape.zlevel) {
if (currentLayer) {
if (currentLayer.needTransform) {
ctx.restore();
}
ctx.flush && ctx.flush();
}
currentZLevel = shape.zlevel;
currentLayer = this.getLayer(currentZLevel);
if (!currentLayer.isBuildin) {
log(
'ZLevel ' + currentZLevel
+ ' has been used by unkown layer ' + currentLayer.id
);
}
ctx = currentLayer.ctx;
// Reset the count
currentLayer.unusedCount = 0;
if (currentLayer.dirty || paintAll) {
currentLayer.clear();
}
if (currentLayer.needTransform) {
ctx.save();
currentLayer.setTransform(ctx);
}
}
if ((currentLayer.dirty || paintAll) && !shape.invisible) {
if (
!shape.onbrush
|| (shape.onbrush && !shape.onbrush(ctx, false))
) {
if (config.catchBrushException) {
try {
shape.brush(ctx, false, this.refreshNextFrame);
}
catch (error) {
log(
error,
'brush error of ' + shape.type,
shape
);
}
}
else {
shape.brush(ctx, false, this.refreshNextFrame);
}
}
}
shape.__dirty = false;
}
if (currentLayer) {
if (currentLayer.needTransform) {
ctx.restore();
}
ctx.flush && ctx.flush();
}
this.eachBuildinLayer(this._postProcessLayer);
};
/**
* 获取 zlevel 所在层,如果不存在则会创建一个新的层
* @param {number} zlevel
* @return {module:zrender/Layer}
*/
Painter.prototype.getLayer = function (zlevel) {
var layer = this._layers[zlevel];
if (!layer) {
// Create a new layer
layer = new Layer(zlevel, this);
layer.isBuildin = true;
if (this._layerConfig[zlevel]) {
util.merge(layer, this._layerConfig[zlevel], true);
}
layer.updateTransform();
this.insertLayer(zlevel, layer);
// Context is created after dom inserted to document
// Or excanvas will get 0px clientWidth and clientHeight
layer.initContext();
}
return layer;
};
Painter.prototype.insertLayer = function (zlevel, layer) {
if (this._layers[zlevel]) {
log('ZLevel ' + zlevel + ' has been used already');
return;
}
// Check if is a valid layer
if (!isLayerValid(layer)) {
log('Layer of zlevel ' + zlevel + ' is not valid');
return;
}
var len = this._zlevelList.length;
var prevLayer = null;
var i = -1;
if (len > 0 && zlevel > this._zlevelList[0]) {
for (i = 0; i < len - 1; i++) {
if (
this._zlevelList[i] < zlevel
&& this._zlevelList[i + 1] > zlevel
) {
break;
}
}
prevLayer = this._layers[this._zlevelList[i]];
}
this._zlevelList.splice(i + 1, 0, zlevel);
var prevDom = prevLayer ? prevLayer.dom : this._bgDom;
if (prevDom.nextSibling) {
prevDom.parentNode.insertBefore(
layer.dom,
prevDom.nextSibling
);
}
else {
prevDom.parentNode.appendChild(layer.dom);
}
this._layers[zlevel] = layer;
};
// Iterate each layer
Painter.prototype.eachLayer = function (cb, context) {
for (var i = 0; i < this._zlevelList.length; i++) {
var z = this._zlevelList[i];
cb.call(context, this._layers[z], z);
}
};
// Iterate each buildin layer
Painter.prototype.eachBuildinLayer = function (cb, context) {
for (var i = 0; i < this._zlevelList.length; i++) {
var z = this._zlevelList[i];
var layer = this._layers[z];
if (layer.isBuildin) {
cb.call(context, layer, z);
}
}
};
// Iterate each other layer except buildin layer
Painter.prototype.eachOtherLayer = function (cb, context) {
for (var i = 0; i < this._zlevelList.length; i++) {
var z = this._zlevelList[i];
var layer = this._layers[z];
if (! layer.isBuildin) {
cb.call(context, layer, z);
}
}
};
/**
* 获取所有已创建的层
* @param {Array.<module:zrender/Layer>} [prevLayer]
*/
Painter.prototype.getLayers = function () {
return this._layers;
};
Painter.prototype._updateLayerStatus = function (list) {
var layers = this._layers;
var elCounts = {};
this.eachBuildinLayer(function (layer, z) {
elCounts[z] = layer.elCount;
layer.elCount = 0;
});
for (var i = 0, l = list.length; i < l; i++) {
var shape = list[i];
var zlevel = shape.zlevel;
var layer = layers[zlevel];
if (layer) {
layer.elCount++;
// 已经被标记为需要刷新
if (layer.dirty) {
continue;
}
layer.dirty = shape.__dirty;
}
}
// 层中的元素数量有发生变化
this.eachBuildinLayer(function (layer, z) {
if (elCounts[z] !== layer.elCount) {
layer.dirty = true;
}
});
};
/**
* 指定的图形列表
* @param {Array.<module:zrender/shape/Base>} shapeList 需要更新的图形元素列表
* @param {Function} [callback] 视图更新后回调函数
*/
Painter.prototype.refreshShapes = function (shapeList, callback) {
for (var i = 0, l = shapeList.length; i < l; i++) {
var shape = shapeList[i];
shape.modSelf();
}
this.refresh(callback);
return this;
};
/**
* 设置loading特效
*
* @param {Object} loadingEffect loading特效
* @return {Painter}
*/
Painter.prototype.setLoadingEffect = function (loadingEffect) {
this._loadingEffect = loadingEffect;
return this;
};
/**
* 清除hover层外所有内容
*/
Painter.prototype.clear = function () {
this.eachBuildinLayer(this._clearLayer);
return this;
};
Painter.prototype._clearLayer = function (layer) {
layer.clear();
};
/**
* 修改指定zlevel的绘制参数
*
* @param {string} zlevel
* @param {Object} config 配置对象
* @param {string} [config.clearColor=0] 每次清空画布的颜色
* @param {string} [config.motionBlur=false] 是否开启动态模糊
* @param {number} [config.lastFrameAlpha=0.7]
* 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
* @param {Array.<number>} [position] 层的平移
* @param {Array.<number>} [rotation] 层的旋转
* @param {Array.<number>} [scale] 层的缩放
* @param {boolean} [zoomable=false] 层是否支持鼠标缩放操作
* @param {boolean} [panable=false] 层是否支持鼠标平移操作
*/
Painter.prototype.modLayer = function (zlevel, config) {
if (config) {
if (!this._layerConfig[zlevel]) {
this._layerConfig[zlevel] = config;
}
else {
util.merge(this._layerConfig[zlevel], config, true);
}
var layer = this._layers[zlevel];
if (layer) {
util.merge(layer, this._layerConfig[zlevel], true);
}
}
};
/**
* 删除指定层
* @param {number} zlevel 层所在的zlevel
*/
Painter.prototype.delLayer = function (zlevel) {
var layer = this._layers[zlevel];
if (!layer) {
return;
}
// Save config
this.modLayer(zlevel, {
position: layer.position,
rotation: layer.rotation,
scale: layer.scale
});
layer.dom.parentNode.removeChild(layer.dom);
delete this._layers[zlevel];
this._zlevelList.splice(util.indexOf(this._zlevelList, zlevel), 1);
};
/**
* 刷新hover层
*/
Painter.prototype.refreshHover = function () {
this.clearHover();
var list = this.storage.getHoverShapes(true);
for (var i = 0, l = list.length; i < l; i++) {
this._brushHover(list[i]);
}
var ctx = this._layers.hover.ctx;
ctx.flush && ctx.flush();
this.storage.delHover();
return this;
};
/**
* 清除hover层所有内容
*/
Painter.prototype.clearHover = function () {
var hover = this._layers.hover;
hover && hover.clear();
return this;
};
/**
* 显示loading
*
* @param {Object=} loadingEffect loading效果对象
*/
Painter.prototype.showLoading = function (loadingEffect) {
this._loadingEffect && this._loadingEffect.stop();
loadingEffect && this.setLoadingEffect(loadingEffect);
this._loadingEffect.start(this);
this.loading = true;
return this;
};
/**
* loading结束
*/
Painter.prototype.hideLoading = function () {
this._loadingEffect.stop();
this.clearHover();
this.loading = false;
return this;
};
/**
* loading结束判断
*/
Painter.prototype.isLoading = function () {
return this.loading;
};
/**
* 区域大小变化后重绘
*/
Painter.prototype.resize = function () {
var domRoot = this._domRoot;
domRoot.style.display = 'none';
var width = this._getWidth();
var height = this._getHeight();
domRoot.style.display = '';
// 优化没有实际改变的resize
if (this._width != width || height != this._height) {
this._width = width;
this._height = height;
domRoot.style.width = width + 'px';
domRoot.style.height = height + 'px';
for (var id in this._layers) {
this._layers[id].resize(width, height);
}
this.refresh(null, true);
}
return this;
};
/**
* 清除单独的一个层
* @param {number} zLevel
*/
Painter.prototype.clearLayer = function (zLevel) {
var layer = this._layers[zLevel];
if (layer) {
layer.clear();
}
};
/**
* 释放
*/
Painter.prototype.dispose = function () {
if (this.isLoading()) {
this.hideLoading();
}
this.root.innerHTML = '';
this.root =
this.storage =
this._domRoot =
this._layers = null;
};
Painter.prototype.getDomHover = function () {
return this._layers.hover.dom;
};
/**
* 图像导出
* @param {string} type
* @param {string} [backgroundColor='#fff'] 背景色
* @return {string} 图片的Base64 url
*/
Painter.prototype.toDataURL = function (type, backgroundColor, args) {
if (window['G_vmlCanvasManager']) {
return null;
}
var imageLayer = new Layer('image', this);
this._bgDom.appendChild(imageLayer.dom);
imageLayer.initContext();
var ctx = imageLayer.ctx;
imageLayer.clearColor = backgroundColor || '#fff';
imageLayer.clear();
var self = this;
// 升序遍历,shape上的zlevel指定绘画图层的z轴层叠
this.storage.iterShape(
function (shape) {
if (!shape.invisible) {
if (!shape.onbrush // 没有onbrush
// 有onbrush并且调用执行返回false或undefined则继续粉刷
|| (shape.onbrush && !shape.onbrush(ctx, false))
) {
if (config.catchBrushException) {
try {
shape.brush(ctx, false, self.refreshNextFrame);
}
catch (error) {
log(
error,
'brush error of ' + shape.type,
shape
);
}
}
else {
shape.brush(ctx, false, self.refreshNextFrame);
}
}
}
},
{ normal: 'up', update: true }
);
var image = imageLayer.dom.toDataURL(type, args);
ctx = null;
this._bgDom.removeChild(imageLayer.dom);
return image;
};
/**
* 获取绘图区域宽度
*/
Painter.prototype.getWidth = function () {
return this._width;
};
/**
* 获取绘图区域高度
*/
Painter.prototype.getHeight = function () {
return this._height;
};
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;
};
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;
};
Painter.prototype._brushHover = function (shape) {
var ctx = this._layers.hover.ctx;
if (!shape.onbrush // 没有onbrush
// 有onbrush并且调用执行返回false或undefined则继续粉刷
|| (shape.onbrush && !shape.onbrush(ctx, true))
) {
var layer = this.getLayer(shape.zlevel);
if (layer.needTransform) {
ctx.save();
layer.setTransform(ctx);
}
// Retina 优化
if (config.catchBrushException) {
try {
shape.brush(ctx, true, this.refreshNextFrame);
}
catch (error) {
log(
error, 'hoverBrush error of ' + shape.type, shape
);
}
}
else {
shape.brush(ctx, true, this.refreshNextFrame);
}
if (layer.needTransform) {
ctx.restore();
}
}
};
Painter.prototype._shapeToImage = function (
id, shape, width, height, devicePixelRatio
) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
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,
image : canvas
}
});
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;
};
Painter.prototype._createShapeToImageProcessor = function () {
if (window['G_vmlCanvasManager']) {
return doNothing;
}
var me = this;
return function (id, e, width, height) {
return me._shapeToImage(
id, e, width, height, config.devicePixelRatio
);
};
};
return Painter;
}
);