diff --git a/player/js/elements/canvasElements/CVContextData.js b/player/js/elements/canvasElements/CVContextData.js index f5d8b93d0..88df6fac9 100644 --- a/player/js/elements/canvasElements/CVContextData.js +++ b/player/js/elements/canvasElements/CVContextData.js @@ -3,28 +3,57 @@ import { } from '../../utils/helpers/arrays'; import Matrix from '../../3rd_party/transformation-matrix'; +function CanvasContext() { + this.opacity = -1; + this.transform = createTypedArray('float32', 16); + this.fillStyle = ''; + this.strokeStyle = ''; + this.lineWidth = ''; + this.lineCap = ''; + this.lineJoin = ''; + this.miterLimit = ''; + this.id = Math.random(); +} + function CVContextData() { - this.saved = []; + this.stack = []; this.cArrPos = 0; this.cTr = new Matrix(); - this.cO = 1; var i; var len = 15; - this.savedOp = createTypedArray('float32', len); for (i = 0; i < len; i += 1) { - this.saved[i] = createTypedArray('float32', 16); + var canvasContext = new CanvasContext(); + this.stack[i] = canvasContext; } this._length = len; + this.nativeContext = null; + this.transformMat = new Matrix(); + this.currentOpacity = 1; + // + this.currentFillStyle = ''; + this.appliedFillStyle = ''; + // + this.currentStrokeStyle = ''; + this.appliedStrokeStyle = ''; + // + this.currentLineWidth = ''; + this.appliedLineWidth = ''; + // + this.currentLineCap = ''; + this.appliedLineCap = ''; + // + this.currentLineJoin = ''; + this.appliedLineJoin = ''; + // + this.appliedMiterLimit = ''; + this.currentMiterLimit = ''; } CVContextData.prototype.duplicate = function () { var newLength = this._length * 2; - var currentSavedOp = this.savedOp; - this.savedOp = createTypedArray('float32', newLength); - this.savedOp.set(currentSavedOp); var i = 0; for (i = this._length; i < newLength; i += 1) { - this.saved[i] = createTypedArray('float32', 16); + this.stack[i] = new CanvasContext(); } this._length = newLength; }; @@ -32,59 +61,179 @@ CVContextData.prototype.duplicate = function () { CVContextData.prototype.reset = function () { this.cArrPos = 0; this.cTr.reset(); - this.cO = 1; + this.stack[this.cArrPos].opacity = 1; }; -CVContextData.prototype.popTransform = function () { - var popped = this.saved[this.cArrPos]; +CVContextData.prototype.restore = function (forceRestore) { + this.cArrPos -= 1; + var currentContext = this.stack[this.cArrPos]; + var transform = currentContext.transform; var i; var arr = this.cTr.props; for (i = 0; i < 16; i += 1) { - arr[i] = popped[i]; + arr[i] = transform[i]; } - return popped; -}; - -CVContextData.prototype.popOpacity = function () { - var popped = this.savedOp[this.cArrPos]; - this.cO = popped; - return popped; -}; - -CVContextData.prototype.pop = function () { - this.cArrPos -= 1; - var transform = this.popTransform(); - var opacity = this.popOpacity(); - return { - transform: transform, - opacity: opacity, - }; + if (forceRestore) { + this.nativeContext.restore(); + var prevStack = this.stack[this.cArrPos + 1]; + this.appliedFillStyle = prevStack.fillStyle; + this.appliedStrokeStyle = prevStack.strokeStyle; + this.appliedLineWidth = prevStack.lineWidth; + this.appliedLineCap = prevStack.lineCap; + this.appliedLineJoin = prevStack.lineJoin; + this.appliedMiterLimit = prevStack.miterLimit; + } + this.nativeContext.setTransform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); + if (forceRestore || (currentContext.opacity !== -1 && this.currentOpacity !== currentContext.opacity)) { + this.nativeContext.globalAlpha = currentContext.opacity; + this.currentOpacity = currentContext.opacity; + } + this.currentFillStyle = currentContext.fillStyle; + this.currentStrokeStyle = currentContext.strokeStyle; + this.currentLineWidth = currentContext.lineWidth; + this.currentLineCap = currentContext.lineCap; + this.currentLineJoin = currentContext.lineJoin; + this.currentMiterLimit = currentContext.miterLimit; }; -CVContextData.prototype.push = function () { +CVContextData.prototype.save = function (saveOnNativeFlag) { + if (saveOnNativeFlag) { + this.nativeContext.save(); + } var props = this.cTr.props; if (this._length <= this.cArrPos) { this.duplicate(); } + + var currentStack = this.stack[this.cArrPos]; var i; - var arr = this.saved[this.cArrPos]; for (i = 0; i < 16; i += 1) { - arr[i] = props[i]; + currentStack.transform[i] = props[i]; } - this.savedOp[this.cArrPos] = this.cO; this.cArrPos += 1; + var newStack = this.stack[this.cArrPos]; + newStack.opacity = currentStack.opacity; + newStack.fillStyle = currentStack.fillStyle; + newStack.strokeStyle = currentStack.strokeStyle; + newStack.lineWidth = currentStack.lineWidth; + newStack.lineCap = currentStack.lineCap; + newStack.lineJoin = currentStack.lineJoin; + newStack.miterLimit = currentStack.miterLimit; }; -CVContextData.prototype.getTransform = function () { - return this.cTr; +CVContextData.prototype.setOpacity = function (value) { + this.stack[this.cArrPos].opacity = value; }; -CVContextData.prototype.getOpacity = function () { - return this.cO; +CVContextData.prototype.setContext = function (value) { + this.nativeContext = value; }; -CVContextData.prototype.setOpacity = function (value) { - this.cO = value; +CVContextData.prototype.fillStyle = function (value) { + if (this.stack[this.cArrPos].fillStyle !== value) { + this.currentFillStyle = value; + this.stack[this.cArrPos].fillStyle = value; + } +}; + +CVContextData.prototype.strokeStyle = function (value) { + if (this.stack[this.cArrPos].strokeStyle !== value) { + this.currentStrokeStyle = value; + this.stack[this.cArrPos].strokeStyle = value; + } +}; + +CVContextData.prototype.lineWidth = function (value) { + if (this.stack[this.cArrPos].lineWidth !== value) { + this.currentLineWidth = value; + this.stack[this.cArrPos].lineWidth = value; + } +}; + +CVContextData.prototype.lineCap = function (value) { + if (this.stack[this.cArrPos].lineCap !== value) { + this.currentLineCap = value; + this.stack[this.cArrPos].lineCap = value; + } +}; + +CVContextData.prototype.lineJoin = function (value) { + if (this.stack[this.cArrPos].lineJoin !== value) { + this.currentLineJoin = value; + this.stack[this.cArrPos].lineJoin = value; + } +}; + +CVContextData.prototype.miterLimit = function (value) { + if (this.stack[this.cArrPos].miterLimit !== value) { + this.currentMiterLimit = value; + this.stack[this.cArrPos].miterLimit = value; + } +}; + +CVContextData.prototype.transform = function (props) { + this.transformMat.cloneFromProps(props); + // Taking the last transform value from the stored stack of transforms + var currentTransform = this.cTr; + // Applying the last transform value after the new transform to respect the order of transformations + this.transformMat.multiply(currentTransform); + // Storing the new transformed value in the stored transform + currentTransform.cloneFromProps(this.transformMat.props); + var trProps = currentTransform.props; + // Applying the new transform to the canvas + this.nativeContext.setTransform(trProps[0], trProps[1], trProps[4], trProps[5], trProps[12], trProps[13]); +}; + +CVContextData.prototype.opacity = function (op) { + var currentOpacity = this.stack[this.cArrPos].opacity; + currentOpacity *= op < 0 ? 0 : op; + if (this.stack[this.cArrPos].opacity !== currentOpacity) { + if (this.currentOpacity !== op) { + this.nativeContext.globalAlpha = op; + this.currentOpacity = op; + } + this.stack[this.cArrPos].opacity = currentOpacity; + } +}; + +CVContextData.prototype.fill = function (rule) { + if (this.appliedFillStyle !== this.currentFillStyle) { + this.appliedFillStyle = this.currentFillStyle; + this.nativeContext.fillStyle = this.appliedFillStyle; + } + this.nativeContext.fill(rule); +}; + +CVContextData.prototype.fillRect = function (x, y, w, h) { + if (this.appliedFillStyle !== this.currentFillStyle) { + this.appliedFillStyle = this.currentFillStyle; + this.nativeContext.fillStyle = this.appliedFillStyle; + } + this.nativeContext.fillRect(x, y, w, h); +}; + +CVContextData.prototype.stroke = function () { + if (this.appliedStrokeStyle !== this.currentStrokeStyle) { + this.appliedStrokeStyle = this.currentStrokeStyle; + this.nativeContext.strokeStyle = this.appliedStrokeStyle; + } + if (this.appliedLineWidth !== this.currentLineWidth) { + this.appliedLineWidth = this.currentLineWidth; + this.nativeContext.lineWidth = this.appliedLineWidth; + } + if (this.appliedLineCap !== this.currentLineCap) { + this.appliedLineCap = this.currentLineCap; + this.nativeContext.lineCap = this.appliedLineCap; + } + if (this.appliedLineJoin !== this.currentLineJoin) { + this.appliedLineJoin = this.currentLineJoin; + this.nativeContext.lineJoin = this.appliedLineJoin; + } + if (this.appliedMiterLimit !== this.currentMiterLimit) { + this.appliedMiterLimit = this.currentMiterLimit; + this.nativeContext.miterLimit = this.appliedMiterLimit; + } + this.nativeContext.stroke(); }; export default CVContextData; diff --git a/player/js/elements/canvasElements/CVShapeElement.js b/player/js/elements/canvasElements/CVShapeElement.js index f37cf51fa..3217bf5d4 100644 --- a/player/js/elements/canvasElements/CVShapeElement.js +++ b/player/js/elements/canvasElements/CVShapeElement.js @@ -295,13 +295,19 @@ CVShapeElement.prototype.drawLayer = function () { renderer.save(); elems = currentStyle.elements; if (type === 'st' || type === 'gs') { - ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd; - ctx.lineWidth = currentStyle.wi; - ctx.lineCap = currentStyle.lc; - ctx.lineJoin = currentStyle.lj; - ctx.miterLimit = currentStyle.ml || 0; + renderer.ctxStrokeStyle(type === 'st' ? currentStyle.co : currentStyle.grd); + // ctx.strokeStyle = type === 'st' ? currentStyle.co : currentStyle.grd; + renderer.ctxLineWidth(currentStyle.wi); + // ctx.lineWidth = currentStyle.wi; + renderer.ctxLineCap(currentStyle.lc); + // ctx.lineCap = currentStyle.lc; + renderer.ctxLineJoin(currentStyle.lj); + // ctx.lineJoin = currentStyle.lj; + renderer.ctxMiterLimit(currentStyle.ml || 0); + // ctx.miterLimit = currentStyle.ml || 0; } else { - ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd; + renderer.ctxFillStyle(type === 'fl' ? currentStyle.co : currentStyle.grd); + // ctx.fillStyle = type === 'fl' ? currentStyle.co : currentStyle.grd; } renderer.ctxOpacity(currentStyle.coOp); if (type !== 'st' && type !== 'gs') { @@ -330,14 +336,16 @@ CVShapeElement.prototype.drawLayer = function () { } } if (type === 'st' || type === 'gs') { - ctx.stroke(); + // ctx.stroke(); + renderer.ctxStroke(); if (currentStyle.da) { ctx.setLineDash(this.dashResetter); } } } if (type !== 'st' && type !== 'gs') { - ctx.fill(currentStyle.r); + // ctx.fill(currentStyle.r); + this.globalData.renderer.ctxFill(currentStyle.r); } renderer.restore(); } diff --git a/player/js/elements/canvasElements/CVSolidElement.js b/player/js/elements/canvasElements/CVSolidElement.js index 61eadd805..23e0d983a 100644 --- a/player/js/elements/canvasElements/CVSolidElement.js +++ b/player/js/elements/canvasElements/CVSolidElement.js @@ -19,9 +19,11 @@ CVSolidElement.prototype.initElement = SVGShapeElement.prototype.initElement; CVSolidElement.prototype.prepareFrame = IImageElement.prototype.prepareFrame; CVSolidElement.prototype.renderInnerContent = function () { - var ctx = this.canvasContext; - ctx.fillStyle = this.data.sc; - ctx.fillRect(0, 0, this.data.sw, this.data.sh); + // var ctx = this.canvasContext; + this.globalData.renderer.ctxFillStyle(this.data.sc); + // ctx.fillStyle = this.data.sc; + this.globalData.renderer.ctxFillRect(0, 0, this.data.sw, this.data.sh); + // ctx.fillRect(0, 0, this.data.sw, this.data.sh); // }; diff --git a/player/js/elements/canvasElements/CVTextElement.js b/player/js/elements/canvasElements/CVTextElement.js index b815e8421..52ef05e37 100644 --- a/player/js/elements/canvasElements/CVTextElement.js +++ b/player/js/elements/canvasElements/CVTextElement.js @@ -130,9 +130,12 @@ CVTextElement.prototype.renderInnerContent = function () { this.validateText(); var ctx = this.canvasContext; ctx.font = this.values.fValue; - ctx.lineCap = 'butt'; - ctx.lineJoin = 'miter'; - ctx.miterLimit = 4; + this.globalData.renderer.ctxLineCap('butt'); + // ctx.lineCap = 'butt'; + this.globalData.renderer.ctxLineJoin('miter'); + // ctx.lineJoin = 'miter'; + this.globalData.renderer.ctxMiterLimit(4); + // ctx.miterLimit = 4; if (!this.data.singleShape) { this.textAnimator.getMeasures(this.textProperty.currentData, this.lettersChangedFlag); @@ -155,23 +158,26 @@ CVTextElement.prototype.renderInnerContent = function () { var lastStrokeW = null; var commands; var pathArr; + var renderer = this.globalData.renderer; for (i = 0; i < len; i += 1) { if (!letters[i].n) { renderedLetter = renderedLetters[i]; if (renderedLetter) { - this.globalData.renderer.save(); - this.globalData.renderer.ctxTransform(renderedLetter.p); - this.globalData.renderer.ctxOpacity(renderedLetter.o); + renderer.save(); + renderer.ctxTransform(renderedLetter.p); + renderer.ctxOpacity(renderedLetter.o); } if (this.fill) { if (renderedLetter && renderedLetter.fc) { if (lastFill !== renderedLetter.fc) { + renderer.ctxFillStyle(renderedLetter.fc); lastFill = renderedLetter.fc; - ctx.fillStyle = renderedLetter.fc; + // ctx.fillStyle = renderedLetter.fc; } } else if (lastFill !== this.values.fill) { lastFill = this.values.fill; - ctx.fillStyle = this.values.fill; + renderer.ctxFillStyle(this.values.fill); + // ctx.fillStyle = this.values.fill; } commands = this.textSpans[i].elem; jLen = commands.length; @@ -185,27 +191,32 @@ CVTextElement.prototype.renderInnerContent = function () { } } this.globalData.canvasContext.closePath(); - this.globalData.canvasContext.fill(); + renderer.ctxFill(); + // this.globalData.canvasContext.fill(); /// ctx.fillText(this.textSpans[i].val,0,0); } if (this.stroke) { if (renderedLetter && renderedLetter.sw) { if (lastStrokeW !== renderedLetter.sw) { lastStrokeW = renderedLetter.sw; - ctx.lineWidth = renderedLetter.sw; + renderer.ctxLineWidth(renderedLetter.sw); + // ctx.lineWidth = renderedLetter.sw; } } else if (lastStrokeW !== this.values.sWidth) { lastStrokeW = this.values.sWidth; - ctx.lineWidth = this.values.sWidth; + renderer.ctxLineWidth(this.values.sWidth); + // ctx.lineWidth = this.values.sWidth; } if (renderedLetter && renderedLetter.sc) { if (lastStroke !== renderedLetter.sc) { lastStroke = renderedLetter.sc; - ctx.strokeStyle = renderedLetter.sc; + renderer.ctxStrokeStyle(renderedLetter.sc); + // ctx.strokeStyle = renderedLetter.sc; } } else if (lastStroke !== this.values.stroke) { lastStroke = this.values.stroke; - ctx.strokeStyle = this.values.stroke; + renderer.ctxStrokeStyle(this.values.stroke); + // ctx.strokeStyle = this.values.stroke; } commands = this.textSpans[i].elem; jLen = commands.length; @@ -219,7 +230,8 @@ CVTextElement.prototype.renderInnerContent = function () { } } this.globalData.canvasContext.closePath(); - this.globalData.canvasContext.stroke(); + renderer.ctxStroke(); + // this.globalData.canvasContext.stroke(); /// ctx.strokeText(letters[i].val,0,0); } if (renderedLetter) { diff --git a/player/js/renderers/CanvasRenderer.js b/player/js/renderers/CanvasRenderer.js index 231d06f06..0a7c98829 100644 --- a/player/js/renderers/CanvasRenderer.js +++ b/player/js/renderers/CanvasRenderer.js @@ -36,6 +36,20 @@ function CanvasRenderer(animationItem, config) { this.transformMat = new Matrix(); this.completeLayers = false; this.rendererType = 'canvas'; + if (this.renderConfig.clearCanvas) { + this.ctxTransform = this.contextData.transform.bind(this.contextData); + this.ctxOpacity = this.contextData.opacity.bind(this.contextData); + this.ctxFillStyle = this.contextData.fillStyle.bind(this.contextData); + this.ctxStrokeStyle = this.contextData.strokeStyle.bind(this.contextData); + this.ctxLineWidth = this.contextData.lineWidth.bind(this.contextData); + this.ctxLineCap = this.contextData.lineCap.bind(this.contextData); + this.ctxLineJoin = this.contextData.lineJoin.bind(this.contextData); + this.ctxMiterLimit = this.contextData.miterLimit.bind(this.contextData); + this.ctxFill = this.contextData.fill.bind(this.contextData); + this.ctxFillRect = this.contextData.fillRect.bind(this.contextData); + this.ctxStroke = this.contextData.stroke.bind(this.contextData); + this.save = this.contextData.save.bind(this.contextData); + } } extendPrototype([CanvasRendererBase], CanvasRenderer); diff --git a/player/js/renderers/CanvasRendererBase.js b/player/js/renderers/CanvasRendererBase.js index 6c801ece6..dd199f511 100644 --- a/player/js/renderers/CanvasRendererBase.js +++ b/player/js/renderers/CanvasRendererBase.js @@ -6,43 +6,13 @@ import { } from '../utils/helpers/arrays'; import createTag from '../utils/helpers/html_elements'; import SVGRenderer from './SVGRenderer'; -import Matrix from '../3rd_party/transformation-matrix'; import BaseRenderer from './BaseRenderer'; -import CVContextData from '../elements/canvasElements/CVContextData'; import CVShapeElement from '../elements/canvasElements/CVShapeElement'; import CVTextElement from '../elements/canvasElements/CVTextElement'; import CVImageElement from '../elements/canvasElements/CVImageElement'; import CVSolidElement from '../elements/canvasElements/CVSolidElement'; -function CanvasRendererBase(animationItem, config) { - this.animationItem = animationItem; - this.renderConfig = { - clearCanvas: (config && config.clearCanvas !== undefined) ? config.clearCanvas : true, - context: (config && config.context) || null, - progressiveLoad: (config && config.progressiveLoad) || false, - preserveAspectRatio: (config && config.preserveAspectRatio) || 'xMidYMid meet', - imagePreserveAspectRatio: (config && config.imagePreserveAspectRatio) || 'xMidYMid slice', - contentVisibility: (config && config.contentVisibility) || 'visible', - className: (config && config.className) || '', - id: (config && config.id) || '', - }; - this.renderConfig.dpr = (config && config.dpr) || 1; - if (this.animationItem.wrapper) { - this.renderConfig.dpr = (config && config.dpr) || window.devicePixelRatio || 1; - } - this.renderedFrame = -1; - this.globalData = { - frameNum: -1, - _mdf: false, - renderConfig: this.renderConfig, - currentGlobalAlpha: -1, - }; - this.contextData = new CVContextData(); - this.elements = []; - this.pendingElements = []; - this.transformMat = new Matrix(); - this.completeLayers = false; - this.rendererType = 'canvas'; +function CanvasRendererBase() { } extendPrototype([BaseRenderer], CanvasRendererBase); @@ -68,39 +38,47 @@ CanvasRendererBase.prototype.ctxTransform = function (props) { if (props[0] === 1 && props[1] === 0 && props[4] === 0 && props[5] === 1 && props[12] === 0 && props[13] === 0) { return; } - if (!this.renderConfig.clearCanvas) { - this.canvasContext.transform(props[0], props[1], props[4], props[5], props[12], props[13]); - return; - } - // Resetting the canvas transform matrix to the new transform - this.transformMat.cloneFromProps(props); - // Taking the last transform value from the stored stack of transforms - var currentTransform = this.contextData.getTransform(); - // Applying the last transform value after the new transform to respect the order of transformations - this.transformMat.multiply(currentTransform); - // Storing the new transformed value in the stored transform - currentTransform.cloneFromProps(this.transformMat.props); - var trProps = currentTransform.props; - // Applying the new transform to the canvas - this.canvasContext.setTransform(trProps[0], trProps[1], trProps[4], trProps[5], trProps[12], trProps[13]); + this.canvasContext.transform(props[0], props[1], props[4], props[5], props[12], props[13]); }; CanvasRendererBase.prototype.ctxOpacity = function (op) { - /* if(op === 1){ - return; - } */ - var currentOpacity = this.contextData.getOpacity(); - if (!this.renderConfig.clearCanvas) { - this.canvasContext.globalAlpha *= op < 0 ? 0 : op; - this.globalData.currentGlobalAlpha = currentOpacity; - return; - } - currentOpacity *= op < 0 ? 0 : op; - this.contextData.setOpacity(currentOpacity); - if (this.globalData.currentGlobalAlpha !== currentOpacity) { - this.canvasContext.globalAlpha = currentOpacity; - this.globalData.currentGlobalAlpha = currentOpacity; - } + this.canvasContext.globalAlpha *= op < 0 ? 0 : op; +}; + +CanvasRendererBase.prototype.ctxFillStyle = function (value) { + this.canvasContext.fillStyle = value; +}; + +CanvasRendererBase.prototype.ctxStrokeStyle = function (value) { + this.canvasContext.strokeStyle = value; +}; + +CanvasRendererBase.prototype.ctxLineWidth = function (value) { + this.canvasContext.lineWidth = value; +}; + +CanvasRendererBase.prototype.ctxLineCap = function (value) { + this.canvasContext.lineCap = value; +}; + +CanvasRendererBase.prototype.ctxLineJoin = function (value) { + this.canvasContext.lineJoin = value; +}; + +CanvasRendererBase.prototype.ctxMiterLimit = function (value) { + this.canvasContext.miterLimit = value; +}; + +CanvasRendererBase.prototype.ctxFill = function (rule) { + this.canvasContext.fill(rule); +}; + +CanvasRendererBase.prototype.ctxFillRect = function (x, y, w, h) { + this.canvasContext.fillRect(x, y, w, h); +}; + +CanvasRendererBase.prototype.ctxStroke = function () { + this.canvasContext.stroke(); }; CanvasRendererBase.prototype.reset = function () { @@ -111,15 +89,8 @@ CanvasRendererBase.prototype.reset = function () { this.contextData.reset(); }; -CanvasRendererBase.prototype.save = function (actionFlag) { - if (!this.renderConfig.clearCanvas) { - this.canvasContext.save(); - return; - } - if (actionFlag) { - this.canvasContext.save(); - } - this.contextData.push(); +CanvasRendererBase.prototype.save = function () { + this.canvasContext.save(); }; CanvasRendererBase.prototype.restore = function (actionFlag) { @@ -128,17 +99,9 @@ CanvasRendererBase.prototype.restore = function (actionFlag) { return; } if (actionFlag) { - this.canvasContext.restore(); this.globalData.blendMode = 'source-over'; } - var popped = this.contextData.pop(); - var transform = popped.transform; - var opacity = popped.opacity; - this.canvasContext.setTransform(transform[0], transform[1], transform[4], transform[5], transform[12], transform[13]); - if (this.globalData.currentGlobalAlpha !== opacity) { - this.canvasContext.globalAlpha = opacity; - this.globalData.currentGlobalAlpha = opacity; - } + this.contextData.restore(actionFlag); }; CanvasRendererBase.prototype.configAnimation = function (animData) { @@ -164,6 +127,7 @@ CanvasRendererBase.prototype.configAnimation = function (animData) { } else { this.canvasContext = this.renderConfig.context; } + this.contextData.setContext(this.canvasContext); this.data = animData; this.layers = animData.layers; this.transformCanvas = { @@ -300,7 +264,7 @@ CanvasRendererBase.prototype.renderFrame = function (num, forceRender) { this.checkLayers(num); } - for (i = 0; i < len; i += 1) { + for (i = len - 1; i >= 0; i -= 1) { if (this.completeLayers || this.elements[i]) { this.elements[i].prepareFrame(num - this.layers[i].st); }