You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
23 KiB
641 lines
23 KiB
import { devicePixelRatio } from '../config.js'; |
|
import * as util from '../core/util.js'; |
|
import Layer from './Layer.js'; |
|
import requestAnimationFrame from '../animation/requestAnimationFrame.js'; |
|
import env from '../core/env.js'; |
|
import { brush, brushSingle } from './graphic.js'; |
|
import { REDRAW_BIT } from '../graphic/constants.js'; |
|
import { getSize } from './helper.js'; |
|
var HOVER_LAYER_ZLEVEL = 1e5; |
|
var CANVAS_ZLEVEL = 314159; |
|
var EL_AFTER_INCREMENTAL_INC = 0.01; |
|
var INCREMENTAL_INC = 0.001; |
|
function isLayerValid(layer) { |
|
if (!layer) { |
|
return false; |
|
} |
|
if (layer.__builtin__) { |
|
return true; |
|
} |
|
if (typeof (layer.resize) !== 'function' |
|
|| typeof (layer.refresh) !== 'function') { |
|
return false; |
|
} |
|
return true; |
|
} |
|
function createRoot(width, height) { |
|
var domRoot = document.createElement('div'); |
|
domRoot.style.cssText = [ |
|
'position:relative', |
|
'width:' + width + 'px', |
|
'height:' + height + 'px', |
|
'padding:0', |
|
'margin:0', |
|
'border-width:0' |
|
].join(';') + ';'; |
|
return domRoot; |
|
} |
|
var CanvasPainter = (function () { |
|
function CanvasPainter(root, storage, opts, id) { |
|
this.type = 'canvas'; |
|
this._zlevelList = []; |
|
this._prevDisplayList = []; |
|
this._layers = {}; |
|
this._layerConfig = {}; |
|
this._needsManuallyCompositing = false; |
|
this.type = 'canvas'; |
|
var singleCanvas = !root.nodeName |
|
|| root.nodeName.toUpperCase() === 'CANVAS'; |
|
this._opts = opts = util.extend({}, opts || {}); |
|
this.dpr = opts.devicePixelRatio || devicePixelRatio; |
|
this._singleCanvas = singleCanvas; |
|
this.root = root; |
|
var rootStyle = root.style; |
|
if (rootStyle) { |
|
util.disableUserSelect(root); |
|
root.innerHTML = ''; |
|
} |
|
this.storage = storage; |
|
var zlevelList = this._zlevelList; |
|
this._prevDisplayList = []; |
|
var layers = this._layers; |
|
if (!singleCanvas) { |
|
this._width = getSize(root, 0, opts); |
|
this._height = getSize(root, 1, opts); |
|
var domRoot = this._domRoot = createRoot(this._width, this._height); |
|
root.appendChild(domRoot); |
|
} |
|
else { |
|
var rootCanvas = root; |
|
var width = rootCanvas.width; |
|
var height = rootCanvas.height; |
|
if (opts.width != null) { |
|
width = opts.width; |
|
} |
|
if (opts.height != null) { |
|
height = opts.height; |
|
} |
|
this.dpr = opts.devicePixelRatio || 1; |
|
rootCanvas.width = width * this.dpr; |
|
rootCanvas.height = height * this.dpr; |
|
this._width = width; |
|
this._height = height; |
|
var mainLayer = new Layer(rootCanvas, this, this.dpr); |
|
mainLayer.__builtin__ = true; |
|
mainLayer.initContext(); |
|
layers[CANVAS_ZLEVEL] = mainLayer; |
|
mainLayer.zlevel = CANVAS_ZLEVEL; |
|
zlevelList.push(CANVAS_ZLEVEL); |
|
this._domRoot = root; |
|
} |
|
} |
|
CanvasPainter.prototype.getType = function () { |
|
return 'canvas'; |
|
}; |
|
CanvasPainter.prototype.isSingleCanvas = function () { |
|
return this._singleCanvas; |
|
}; |
|
CanvasPainter.prototype.getViewportRoot = function () { |
|
return this._domRoot; |
|
}; |
|
CanvasPainter.prototype.getViewportRootOffset = function () { |
|
var viewportRoot = this.getViewportRoot(); |
|
if (viewportRoot) { |
|
return { |
|
offsetLeft: viewportRoot.offsetLeft || 0, |
|
offsetTop: viewportRoot.offsetTop || 0 |
|
}; |
|
} |
|
}; |
|
CanvasPainter.prototype.refresh = function (paintAll) { |
|
var list = this.storage.getDisplayList(true); |
|
var prevList = this._prevDisplayList; |
|
var zlevelList = this._zlevelList; |
|
this._redrawId = Math.random(); |
|
this._paintList(list, prevList, paintAll, this._redrawId); |
|
for (var i = 0; i < zlevelList.length; i++) { |
|
var z = zlevelList[i]; |
|
var layer = this._layers[z]; |
|
if (!layer.__builtin__ && layer.refresh) { |
|
var clearColor = i === 0 ? this._backgroundColor : null; |
|
layer.refresh(clearColor); |
|
} |
|
} |
|
if (this._opts.useDirtyRect) { |
|
this._prevDisplayList = list.slice(); |
|
} |
|
return this; |
|
}; |
|
CanvasPainter.prototype.refreshHover = function () { |
|
this._paintHoverList(this.storage.getDisplayList(false)); |
|
}; |
|
CanvasPainter.prototype._paintHoverList = function (list) { |
|
var len = list.length; |
|
var hoverLayer = this._hoverlayer; |
|
hoverLayer && hoverLayer.clear(); |
|
if (!len) { |
|
return; |
|
} |
|
var scope = { |
|
inHover: true, |
|
viewWidth: this._width, |
|
viewHeight: this._height |
|
}; |
|
var ctx; |
|
for (var i = 0; i < len; i++) { |
|
var el = list[i]; |
|
if (el.__inHover) { |
|
if (!hoverLayer) { |
|
hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); |
|
} |
|
if (!ctx) { |
|
ctx = hoverLayer.ctx; |
|
ctx.save(); |
|
} |
|
brush(ctx, el, scope, i === len - 1); |
|
} |
|
} |
|
if (ctx) { |
|
ctx.restore(); |
|
} |
|
}; |
|
CanvasPainter.prototype.getHoverLayer = function () { |
|
return this.getLayer(HOVER_LAYER_ZLEVEL); |
|
}; |
|
CanvasPainter.prototype.paintOne = function (ctx, el) { |
|
brushSingle(ctx, el); |
|
}; |
|
CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) { |
|
if (this._redrawId !== redrawId) { |
|
return; |
|
} |
|
paintAll = paintAll || false; |
|
this._updateLayerStatus(list); |
|
var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover; |
|
if (this._needsManuallyCompositing) { |
|
this._compositeManually(); |
|
} |
|
if (needsRefreshHover) { |
|
this._paintHoverList(list); |
|
} |
|
if (!finished) { |
|
var self_1 = this; |
|
requestAnimationFrame(function () { |
|
self_1._paintList(list, prevList, paintAll, redrawId); |
|
}); |
|
} |
|
else { |
|
this.eachLayer(function (layer) { |
|
layer.afterBrush && layer.afterBrush(); |
|
}); |
|
} |
|
}; |
|
CanvasPainter.prototype._compositeManually = function () { |
|
var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; |
|
var width = this._domRoot.width; |
|
var height = this._domRoot.height; |
|
ctx.clearRect(0, 0, width, height); |
|
this.eachBuiltinLayer(function (layer) { |
|
if (layer.virtual) { |
|
ctx.drawImage(layer.dom, 0, 0, width, height); |
|
} |
|
}); |
|
}; |
|
CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) { |
|
var _this = this; |
|
var layerList = []; |
|
var useDirtyRect = this._opts.useDirtyRect; |
|
for (var zi = 0; zi < this._zlevelList.length; zi++) { |
|
var zlevel = this._zlevelList[zi]; |
|
var layer = this._layers[zlevel]; |
|
if (layer.__builtin__ |
|
&& layer !== this._hoverlayer |
|
&& (layer.__dirty || paintAll)) { |
|
layerList.push(layer); |
|
} |
|
} |
|
var finished = true; |
|
var needsRefreshHover = false; |
|
var _loop_1 = function (k) { |
|
var layer = layerList[k]; |
|
var ctx = layer.ctx; |
|
var repaintRects = useDirtyRect |
|
&& layer.createRepaintRects(list, prevList, this_1._width, this_1._height); |
|
var start = paintAll ? layer.__startIndex : layer.__drawIndex; |
|
var useTimer = !paintAll && layer.incremental && Date.now; |
|
var startTime = useTimer && Date.now(); |
|
var clearColor = layer.zlevel === this_1._zlevelList[0] |
|
? this_1._backgroundColor : null; |
|
if (layer.__startIndex === layer.__endIndex) { |
|
layer.clear(false, clearColor, repaintRects); |
|
} |
|
else if (start === layer.__startIndex) { |
|
var firstEl = list[start]; |
|
if (!firstEl.incremental || !firstEl.notClear || paintAll) { |
|
layer.clear(false, clearColor, repaintRects); |
|
} |
|
} |
|
if (start === -1) { |
|
console.error('For some unknown reason. drawIndex is -1'); |
|
start = layer.__startIndex; |
|
} |
|
var i; |
|
var repaint = function (repaintRect) { |
|
var scope = { |
|
inHover: false, |
|
allClipped: false, |
|
prevEl: null, |
|
viewWidth: _this._width, |
|
viewHeight: _this._height |
|
}; |
|
for (i = start; i < layer.__endIndex; i++) { |
|
var el = list[i]; |
|
if (el.__inHover) { |
|
needsRefreshHover = true; |
|
} |
|
_this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1); |
|
if (useTimer) { |
|
var dTime = Date.now() - startTime; |
|
if (dTime > 15) { |
|
break; |
|
} |
|
} |
|
} |
|
if (scope.prevElClipPaths) { |
|
ctx.restore(); |
|
} |
|
}; |
|
if (repaintRects) { |
|
if (repaintRects.length === 0) { |
|
i = layer.__endIndex; |
|
} |
|
else { |
|
var dpr = this_1.dpr; |
|
for (var r = 0; r < repaintRects.length; ++r) { |
|
var rect = repaintRects[r]; |
|
ctx.save(); |
|
ctx.beginPath(); |
|
ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr); |
|
ctx.clip(); |
|
repaint(rect); |
|
ctx.restore(); |
|
} |
|
} |
|
} |
|
else { |
|
ctx.save(); |
|
repaint(); |
|
ctx.restore(); |
|
} |
|
layer.__drawIndex = i; |
|
if (layer.__drawIndex < layer.__endIndex) { |
|
finished = false; |
|
} |
|
}; |
|
var this_1 = this; |
|
for (var k = 0; k < layerList.length; k++) { |
|
_loop_1(k); |
|
} |
|
if (env.wxa) { |
|
util.each(this._layers, function (layer) { |
|
if (layer && layer.ctx && layer.ctx.draw) { |
|
layer.ctx.draw(); |
|
} |
|
}); |
|
} |
|
return { |
|
finished: finished, |
|
needsRefreshHover: needsRefreshHover |
|
}; |
|
}; |
|
CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) { |
|
var ctx = currentLayer.ctx; |
|
if (useDirtyRect) { |
|
var paintRect = el.getPaintRect(); |
|
if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) { |
|
brush(ctx, el, scope, isLast); |
|
el.setPrevPaintRect(paintRect); |
|
} |
|
} |
|
else { |
|
brush(ctx, el, scope, isLast); |
|
} |
|
}; |
|
CanvasPainter.prototype.getLayer = function (zlevel, virtual) { |
|
if (this._singleCanvas && !this._needsManuallyCompositing) { |
|
zlevel = CANVAS_ZLEVEL; |
|
} |
|
var layer = this._layers[zlevel]; |
|
if (!layer) { |
|
layer = new Layer('zr_' + zlevel, this, this.dpr); |
|
layer.zlevel = zlevel; |
|
layer.__builtin__ = true; |
|
if (this._layerConfig[zlevel]) { |
|
util.merge(layer, this._layerConfig[zlevel], true); |
|
} |
|
else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) { |
|
util.merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true); |
|
} |
|
if (virtual) { |
|
layer.virtual = virtual; |
|
} |
|
this.insertLayer(zlevel, layer); |
|
layer.initContext(); |
|
} |
|
return layer; |
|
}; |
|
CanvasPainter.prototype.insertLayer = function (zlevel, layer) { |
|
var layersMap = this._layers; |
|
var zlevelList = this._zlevelList; |
|
var len = zlevelList.length; |
|
var domRoot = this._domRoot; |
|
var prevLayer = null; |
|
var i = -1; |
|
if (layersMap[zlevel]) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
util.logError('ZLevel ' + zlevel + ' has been used already'); |
|
} |
|
return; |
|
} |
|
if (!isLayerValid(layer)) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
util.logError('Layer of zlevel ' + zlevel + ' is not valid'); |
|
} |
|
return; |
|
} |
|
if (len > 0 && zlevel > zlevelList[0]) { |
|
for (i = 0; i < len - 1; i++) { |
|
if (zlevelList[i] < zlevel |
|
&& zlevelList[i + 1] > zlevel) { |
|
break; |
|
} |
|
} |
|
prevLayer = layersMap[zlevelList[i]]; |
|
} |
|
zlevelList.splice(i + 1, 0, zlevel); |
|
layersMap[zlevel] = layer; |
|
if (!layer.virtual) { |
|
if (prevLayer) { |
|
var prevDom = prevLayer.dom; |
|
if (prevDom.nextSibling) { |
|
domRoot.insertBefore(layer.dom, prevDom.nextSibling); |
|
} |
|
else { |
|
domRoot.appendChild(layer.dom); |
|
} |
|
} |
|
else { |
|
if (domRoot.firstChild) { |
|
domRoot.insertBefore(layer.dom, domRoot.firstChild); |
|
} |
|
else { |
|
domRoot.appendChild(layer.dom); |
|
} |
|
} |
|
} |
|
layer.__painter = this; |
|
}; |
|
CanvasPainter.prototype.eachLayer = function (cb, context) { |
|
var zlevelList = this._zlevelList; |
|
for (var i = 0; i < zlevelList.length; i++) { |
|
var z = zlevelList[i]; |
|
cb.call(context, this._layers[z], z); |
|
} |
|
}; |
|
CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) { |
|
var zlevelList = this._zlevelList; |
|
for (var i = 0; i < zlevelList.length; i++) { |
|
var z = zlevelList[i]; |
|
var layer = this._layers[z]; |
|
if (layer.__builtin__) { |
|
cb.call(context, layer, z); |
|
} |
|
} |
|
}; |
|
CanvasPainter.prototype.eachOtherLayer = function (cb, context) { |
|
var zlevelList = this._zlevelList; |
|
for (var i = 0; i < zlevelList.length; i++) { |
|
var z = zlevelList[i]; |
|
var layer = this._layers[z]; |
|
if (!layer.__builtin__) { |
|
cb.call(context, layer, z); |
|
} |
|
} |
|
}; |
|
CanvasPainter.prototype.getLayers = function () { |
|
return this._layers; |
|
}; |
|
CanvasPainter.prototype._updateLayerStatus = function (list) { |
|
this.eachBuiltinLayer(function (layer, z) { |
|
layer.__dirty = layer.__used = false; |
|
}); |
|
function updatePrevLayer(idx) { |
|
if (prevLayer) { |
|
if (prevLayer.__endIndex !== idx) { |
|
prevLayer.__dirty = true; |
|
} |
|
prevLayer.__endIndex = idx; |
|
} |
|
} |
|
if (this._singleCanvas) { |
|
for (var i_1 = 1; i_1 < list.length; i_1++) { |
|
var el = list[i_1]; |
|
if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) { |
|
this._needsManuallyCompositing = true; |
|
break; |
|
} |
|
} |
|
} |
|
var prevLayer = null; |
|
var incrementalLayerCount = 0; |
|
var prevZlevel; |
|
var i; |
|
for (i = 0; i < list.length; i++) { |
|
var el = list[i]; |
|
var zlevel = el.zlevel; |
|
var layer = void 0; |
|
if (prevZlevel !== zlevel) { |
|
prevZlevel = zlevel; |
|
incrementalLayerCount = 0; |
|
} |
|
if (el.incremental) { |
|
layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); |
|
layer.incremental = true; |
|
incrementalLayerCount = 1; |
|
} |
|
else { |
|
layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); |
|
} |
|
if (!layer.__builtin__) { |
|
util.logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id); |
|
} |
|
if (layer !== prevLayer) { |
|
layer.__used = true; |
|
if (layer.__startIndex !== i) { |
|
layer.__dirty = true; |
|
} |
|
layer.__startIndex = i; |
|
if (!layer.incremental) { |
|
layer.__drawIndex = i; |
|
} |
|
else { |
|
layer.__drawIndex = -1; |
|
} |
|
updatePrevLayer(i); |
|
prevLayer = layer; |
|
} |
|
if ((el.__dirty & REDRAW_BIT) && !el.__inHover) { |
|
layer.__dirty = true; |
|
if (layer.incremental && layer.__drawIndex < 0) { |
|
layer.__drawIndex = i; |
|
} |
|
} |
|
} |
|
updatePrevLayer(i); |
|
this.eachBuiltinLayer(function (layer, z) { |
|
if (!layer.__used && layer.getElementCount() > 0) { |
|
layer.__dirty = true; |
|
layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; |
|
} |
|
if (layer.__dirty && layer.__drawIndex < 0) { |
|
layer.__drawIndex = layer.__startIndex; |
|
} |
|
}); |
|
}; |
|
CanvasPainter.prototype.clear = function () { |
|
this.eachBuiltinLayer(this._clearLayer); |
|
return this; |
|
}; |
|
CanvasPainter.prototype._clearLayer = function (layer) { |
|
layer.clear(); |
|
}; |
|
CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) { |
|
this._backgroundColor = backgroundColor; |
|
util.each(this._layers, function (layer) { |
|
layer.setUnpainted(); |
|
}); |
|
}; |
|
CanvasPainter.prototype.configLayer = function (zlevel, config) { |
|
if (config) { |
|
var layerConfig = this._layerConfig; |
|
if (!layerConfig[zlevel]) { |
|
layerConfig[zlevel] = config; |
|
} |
|
else { |
|
util.merge(layerConfig[zlevel], config, true); |
|
} |
|
for (var i = 0; i < this._zlevelList.length; i++) { |
|
var _zlevel = this._zlevelList[i]; |
|
if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { |
|
var layer = this._layers[_zlevel]; |
|
util.merge(layer, layerConfig[zlevel], true); |
|
} |
|
} |
|
} |
|
}; |
|
CanvasPainter.prototype.delLayer = function (zlevel) { |
|
var layers = this._layers; |
|
var zlevelList = this._zlevelList; |
|
var layer = layers[zlevel]; |
|
if (!layer) { |
|
return; |
|
} |
|
layer.dom.parentNode.removeChild(layer.dom); |
|
delete layers[zlevel]; |
|
zlevelList.splice(util.indexOf(zlevelList, zlevel), 1); |
|
}; |
|
CanvasPainter.prototype.resize = function (width, height) { |
|
if (!this._domRoot.style) { |
|
if (width == null || height == null) { |
|
return; |
|
} |
|
this._width = width; |
|
this._height = height; |
|
this.getLayer(CANVAS_ZLEVEL).resize(width, height); |
|
} |
|
else { |
|
var domRoot = this._domRoot; |
|
domRoot.style.display = 'none'; |
|
var opts = this._opts; |
|
var root = this.root; |
|
width != null && (opts.width = width); |
|
height != null && (opts.height = height); |
|
width = getSize(root, 0, opts); |
|
height = getSize(root, 1, opts); |
|
domRoot.style.display = ''; |
|
if (this._width !== width || height !== this._height) { |
|
domRoot.style.width = width + 'px'; |
|
domRoot.style.height = height + 'px'; |
|
for (var id in this._layers) { |
|
if (this._layers.hasOwnProperty(id)) { |
|
this._layers[id].resize(width, height); |
|
} |
|
} |
|
this.refresh(true); |
|
} |
|
this._width = width; |
|
this._height = height; |
|
} |
|
return this; |
|
}; |
|
CanvasPainter.prototype.clearLayer = function (zlevel) { |
|
var layer = this._layers[zlevel]; |
|
if (layer) { |
|
layer.clear(); |
|
} |
|
}; |
|
CanvasPainter.prototype.dispose = function () { |
|
this.root.innerHTML = ''; |
|
this.root = |
|
this.storage = |
|
this._domRoot = |
|
this._layers = null; |
|
}; |
|
CanvasPainter.prototype.getRenderedCanvas = function (opts) { |
|
opts = opts || {}; |
|
if (this._singleCanvas && !this._compositeManually) { |
|
return this._layers[CANVAS_ZLEVEL].dom; |
|
} |
|
var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); |
|
imageLayer.initContext(); |
|
imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); |
|
var ctx = imageLayer.ctx; |
|
if (opts.pixelRatio <= this.dpr) { |
|
this.refresh(); |
|
var width_1 = imageLayer.dom.width; |
|
var height_1 = imageLayer.dom.height; |
|
this.eachLayer(function (layer) { |
|
if (layer.__builtin__) { |
|
ctx.drawImage(layer.dom, 0, 0, width_1, height_1); |
|
} |
|
else if (layer.renderToCanvas) { |
|
ctx.save(); |
|
layer.renderToCanvas(ctx); |
|
ctx.restore(); |
|
} |
|
}); |
|
} |
|
else { |
|
var scope = { |
|
inHover: false, |
|
viewWidth: this._width, |
|
viewHeight: this._height |
|
}; |
|
var displayList = this.storage.getDisplayList(true); |
|
for (var i = 0, len = displayList.length; i < len; i++) { |
|
var el = displayList[i]; |
|
brush(ctx, el, scope, i === len - 1); |
|
} |
|
} |
|
return imageLayer.dom; |
|
}; |
|
CanvasPainter.prototype.getWidth = function () { |
|
return this._width; |
|
}; |
|
CanvasPainter.prototype.getHeight = function () { |
|
return this._height; |
|
}; |
|
return CanvasPainter; |
|
}()); |
|
export default CanvasPainter; |
|
;
|
|
|