var lottiejs = (function(window) { "use strict"; var svgNS = "http://www.w3.org/2000/svg"; var locationHref = ''; var initialDefaultFrame = -999999; var subframeEnabled = true; var expressionsPlugin; var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); var cachedColors = {}; var bm_rounder = Math.round; var bm_rnd; var bm_pow = Math.pow; var bm_sqrt = Math.sqrt; var bm_abs = Math.abs; var bm_floor = Math.floor; var bm_max = Math.max; var bm_min = Math.min; var blitter = 10; var BMMath = {}; (function(){ var propertyNames = ["abs", "acos", "acosh", "asin", "asinh", "atan", "atanh", "atan2", "ceil", "cbrt", "expm1", "clz32", "cos", "cosh", "exp", "floor", "fround", "hypot", "imul", "log", "log1p", "log2", "log10", "max", "min", "pow", "random", "round", "sign", "sin", "sinh", "sqrt", "tan", "tanh", "trunc", "E", "LN10", "LN2", "LOG10E", "LOG2E", "PI", "SQRT1_2", "SQRT2"]; var i, len = propertyNames.length; for(i=0;i 1) { hsv[1] = 1; } else if (hsv[1] <= 0) { hsv[1] = 0; } return HSVtoRGB(hsv[0],hsv[1],hsv[2]); } function addBrightnessToRGB(color,offset){ var hsv = RGBtoHSV(color[0]*255,color[1]*255,color[2]*255); hsv[2] += offset; if (hsv[2] > 1) { hsv[2] = 1; } else if (hsv[2] < 0) { hsv[2] = 0; } return HSVtoRGB(hsv[0],hsv[1],hsv[2]); } function addHueToRGB(color,offset) { var hsv = RGBtoHSV(color[0]*255,color[1]*255,color[2]*255); hsv[0] += offset/360; if (hsv[0] > 1) { hsv[0] -= 1; } else if (hsv[0] < 0) { hsv[0] += 1; } return HSVtoRGB(hsv[0],hsv[1],hsv[2]); } var rgbToHex = (function(){ var colorMap = []; var i; var hex; for(i=0;i<256;i+=1){ hex = i.toString(16); colorMap[i] = hex.length == 1 ? '0' + hex : hex; } return function(r, g, b) { if(r<0){ r = 0; } if(g<0){ g = 0; } if(b<0){ b = 0; } return '#' + colorMap[r] + colorMap[g] + colorMap[b]; }; }()); function BaseEvent(){} BaseEvent.prototype = { triggerEvent: function (eventName, args) { if (this._cbs[eventName]) { var len = this._cbs[eventName].length; for (var i = 0; i < len; i++){ this._cbs[eventName][i](args); } } }, addEventListener: function (eventName, callback) { if (!this._cbs[eventName]){ this._cbs[eventName] = []; } this._cbs[eventName].push(callback); return function() { this.removeEventListener(eventName, callback); }.bind(this); }, removeEventListener: function (eventName,callback){ if (!callback){ this._cbs[eventName] = null; }else if(this._cbs[eventName]){ var i = 0, len = this._cbs[eventName].length; while(i 0) || (val > -0.000001 && val < 0)) { return _rnd(val * v) / v; } return val; } function to2dCSS() { //Doesn't make much sense to add this optimization. If it is an identity matrix, it's very likely this will get called only once since it won't be keyframed. /*if(this.isIdentity()) { return ''; }*/ var props = this.props; var _a = roundMatrixProperty(props[0]); var _b = roundMatrixProperty(props[1]); var _c = roundMatrixProperty(props[4]); var _d = roundMatrixProperty(props[5]); var _e = roundMatrixProperty(props[12]); var _f = roundMatrixProperty(props[13]); return "matrix(" + _a + ',' + _b + ',' + _c + ',' + _d + ',' + _e + ',' + _f + ")"; } return function(){ this.reset = reset; this.rotate = rotate; this.rotateX = rotateX; this.rotateY = rotateY; this.rotateZ = rotateZ; this.skew = skew; this.skewFromAxis = skewFromAxis; this.shear = shear; this.scale = scale; this.setTransform = setTransform; this.translate = translate; this.transform = transform; this.applyToPoint = applyToPoint; this.applyToX = applyToX; this.applyToY = applyToY; this.applyToZ = applyToZ; this.applyToPointArray = applyToPointArray; this.applyToTriplePoints = applyToTriplePoints; this.applyToPointStringified = applyToPointStringified; this.toCSS = toCSS; this.to2dCSS = to2dCSS; this.clone = clone; this.cloneFromProps = cloneFromProps; this.equals = equals; this.inversePoints = inversePoints; this.inversePoint = inversePoint; this._t = this.transform; this.isIdentity = isIdentity; this._identity = true; this._identityCalculated = false; this.props = createTypedArray('float32', 16); this.reset(); }; }()); (function (pool, math) { // // The following constants are related to IEEE 754 limits. // var global = this, width = 256, // each RC4 output is 0 <= x < 256 chunks = 6, // at least six RC4 outputs for each double digits = 52, // there are 52 significant digits in a double rngname = 'random', // rngname: name for Math.random and Math.seedrandom startdenom = math.pow(width, chunks), significance = math.pow(2, digits), overflow = significance * 2, mask = width - 1, nodecrypto; // node.js crypto module, initialized at the bottom. // // seedrandom() // This is the seedrandom function described above. // function seedrandom(seed, options, callback) { var key = []; options = (options === true) ? { entropy: true } : (options || {}); // Flatten the seed string or build one from local entropy if needed. var shortseed = mixkey(flatten( options.entropy ? [seed, tostring(pool)] : (seed === null) ? autoseed() : seed, 3), key); // Use the seed to initialize an ARC4 generator. var arc4 = new ARC4(key); // This function returns a random double in [0, 1) that contains // randomness in every bit of the mantissa of the IEEE 754 value. var prng = function() { var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 d = startdenom, // and denominator d = 2 ^ 48. x = 0; // and no 'extra last byte'. while (n < significance) { // Fill up all significant digits by n = (n + x) * width; // shifting numerator and d *= width; // denominator and generating a x = arc4.g(1); // new least-significant-byte. } while (n >= overflow) { // To avoid rounding up, before adding n /= 2; // last byte, shift everything d /= 2; // right using integer math until x >>>= 1; // we have exactly the desired bits. } return (n + x) / d; // Form the number within [0, 1). }; prng.int32 = function() { return arc4.g(4) | 0; }; prng.quick = function() { return arc4.g(4) / 0x100000000; }; prng.double = prng; // Mix the randomness into accumulated entropy. mixkey(tostring(arc4.S), pool); // Calling convention: what to return as a function of prng, seed, is_math. return (options.pass || callback || function(prng, seed, is_math_call, state) { if (state) { // Load the arc4 state from the given state if it has an S array. if (state.S) { copy(state, arc4); } // Only provide the .state method if requested via options.state. prng.state = function() { return copy(arc4, {}); }; } // If called as a method of Math (Math.seedrandom()), mutate // Math.random because that is how seedrandom.js has worked since v1.0. if (is_math_call) { math[rngname] = prng; return seed; } // Otherwise, it is a newer calling convention, so return the // prng directly. else return prng; })( prng, shortseed, 'global' in options ? options.global : (this == math), options.state); } math['seed' + rngname] = seedrandom; // // ARC4 // // An ARC4 implementation. The constructor takes a key in the form of // an array of at most (width) integers that should be 0 <= x < (width). // // The g(count) method returns a pseudorandom integer that concatenates // the next (count) outputs from ARC4. Its return value is a number x // that is in the range 0 <= x < (width ^ count). // function ARC4(key) { var t, keylen = key.length, me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; // The empty key [] is treated as [0]. if (!keylen) { key = [keylen++]; } // Set up S using the standard key scheduling algorithm. while (i < width) { s[i] = i++; } for (i = 0; i < width; i++) { s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))]; s[j] = t; } // The "g" method returns the next (count) outputs as one number. me.g = function(count) { // Using instance members instead of closure state nearly doubles speed. var t, r = 0, i = me.i, j = me.j, s = me.S; while (count--) { t = s[i = mask & (i + 1)]; r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))]; } me.i = i; me.j = j; return r; // For robust unpredictability, the function call below automatically // discards an initial batch of values. This is called RC4-drop[256]. // See http://google.com/search?q=rsa+fluhrer+response&btnI }; } // // copy() // Copies internal state of ARC4 to or from a plain object. // function copy(f, t) { t.i = f.i; t.j = f.j; t.S = f.S.slice(); return t; } // // flatten() // Converts an object tree to nested arrays of strings. // function flatten(obj, depth) { var result = [], typ = (typeof obj), prop; if (depth && typ == 'object') { for (prop in obj) { try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} } } return (result.length ? result : typ == 'string' ? obj : obj + '\0'); } // // mixkey() // Mixes a string seed into a key that is an array of integers, and // returns a shortened string seed that is equivalent to the result key. // function mixkey(seed, key) { var stringseed = seed + '', smear, j = 0; while (j < stringseed.length) { key[mask & j] = mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); } return tostring(key); } // // autoseed() // Returns an object for autoseeding, using window.crypto and Node crypto // module if available. // function autoseed() { try { if (nodecrypto) { return tostring(nodecrypto.randomBytes(width)); } var out = new Uint8Array(width); (global.crypto || global.msCrypto).getRandomValues(out); return tostring(out); } catch (e) { var browser = global.navigator, plugins = browser && browser.plugins; return [+new Date(), global, plugins, global.screen, tostring(pool)]; } } // // tostring() // Converts an array of charcodes to a string // function tostring(a) { return String.fromCharCode.apply(0, a); } // // When seedrandom.js is loaded, we immediately mix a few bits // from the built-in RNG into the entropy pool. Because we do // not want to interfere with deterministic PRNG state later, // seedrandom will not call math.random on its own again after // initialization. // mixkey(math.random(), pool); // // Nodejs and AMD support: export the implementation as a module using // either convention. // // End anonymous scope, and pass initial values. })( [], // pool: entropy pool starts empty BMMath // math: package containing random, pow, and seedrandom ); var BezierFactory = (function(){ var ob = {}; ob.getBezierEasing = getBezierEasing; var beziers = {}; function getBezierEasing(a,b,c,d,nm){ var str = nm || ('bez_' + a+'_'+b+'_'+c+'_'+d).replace(/\./g, 'p'); if(beziers[str]){ return beziers[str]; } var bezEasing = new BezierEasing([a,b,c,d]); beziers[str] = bezEasing; return bezEasing; } // These values are established by empiricism with tests (tradeoff: performance VS precision) var NEWTON_ITERATIONS = 4; var NEWTON_MIN_SLOPE = 0.001; var SUBDIVISION_PRECISION = 0.0000001; var SUBDIVISION_MAX_ITERATIONS = 10; var kSplineTableSize = 11; var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); var float32ArraySupported = typeof Float32Array === "function"; function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C (aA1) { return 3.0 * aA1; } // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT; } // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function binarySubdivide (aX, aA, aB, mX1, mX2) { var currentX, currentT, i = 0; do { currentT = aA + (aB - aA) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - aX; if (currentX > 0.0) { aB = currentT; } else { aA = currentT; } } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return currentT; } function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { for (var i = 0; i < NEWTON_ITERATIONS; ++i) { var currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) return aGuessT; var currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } /** * points is an array of [ mX1, mY1, mX2, mY2 ] */ function BezierEasing (points) { this._p = points; this._mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); this._precomputed = false; this.get = this.get.bind(this); } BezierEasing.prototype = { get: function (x) { var mX1 = this._p[0], mY1 = this._p[1], mX2 = this._p[2], mY2 = this._p[3]; if (!this._precomputed) this._precompute(); if (mX1 === mY1 && mX2 === mY2) return x; // linear // Because JavaScript number are imprecise, we should guarantee the extremes are right. if (x === 0) return 0; if (x === 1) return 1; return calcBezier(this._getTForX(x), mY1, mY2); }, // Private part _precompute: function () { var mX1 = this._p[0], mY1 = this._p[1], mX2 = this._p[2], mY2 = this._p[3]; this._precomputed = true; if (mX1 !== mY1 || mX2 !== mY2) this._calcSampleValues(); }, _calcSampleValues: function () { var mX1 = this._p[0], mX2 = this._p[2]; for (var i = 0; i < kSplineTableSize; ++i) { this._mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); } }, /** * getTForX chose the fastest heuristic to determine the percentage value precisely from a given X projection. */ _getTForX: function (aX) { var mX1 = this._p[0], mX2 = this._p[2], mSampleValues = this._mSampleValues; var intervalStart = 0.0; var currentSample = 1; var lastSample = kSplineTableSize - 1; for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) { intervalStart += kSampleStepSize; } --currentSample; // Interpolate to provide an initial guess for t var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] - mSampleValues[currentSample]); var guessForT = intervalStart + dist * kSampleStepSize; var initialSlope = getSlope(guessForT, mX1, mX2); if (initialSlope >= NEWTON_MIN_SLOPE) { return newtonRaphsonIterate(aX, guessForT, mX1, mX2); } else if (initialSlope === 0.0) { return guessForT; } else { return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); } } }; return ob; }()); (function () { var lastTime = 0; var vendors = ['ms', 'moz', 'webkit', 'o']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; } if(!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = setTimeout(function () { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; if(!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) { clearTimeout(id); }; }()); function extendPrototype(sources,destination){ var i, len = sources.length, sourcePrototype; for (i = 0;i < len;i += 1) { sourcePrototype = sources[i].prototype; for (var attr in sourcePrototype) { if (sourcePrototype.hasOwnProperty(attr)) destination.prototype[attr] = sourcePrototype[attr]; } } } function getDescriptor(object, prop) { return Object.getOwnPropertyDescriptor(object, prop); } function createProxyFunction(prototype) { function ProxyFunction(){} ProxyFunction.prototype = prototype; return ProxyFunction; } function bezFunction(){ var easingFunctions = []; var math = Math; function pointOnLine2D(x1,y1, x2,y2, x3,y3){ var det1 = (x1*y2) + (y1*x3) + (x2*y3) - (x3*y2) - (y3*x1) - (x2*y1); return det1 > -0.001 && det1 < 0.001; } function pointOnLine3D(x1,y1,z1, x2,y2,z2, x3,y3,z3){ if(z1 === 0 && z2 === 0 && z3 === 0) { return pointOnLine2D(x1,y1, x2,y2, x3,y3); } var dist1 = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) + Math.pow(z2 - z1, 2)); var dist2 = Math.sqrt(Math.pow(x3 - x1, 2) + Math.pow(y3 - y1, 2) + Math.pow(z3 - z1, 2)); var dist3 = Math.sqrt(Math.pow(x3 - x2, 2) + Math.pow(y3 - y2, 2) + Math.pow(z3 - z2, 2)); var diffDist; if(dist1 > dist2){ if(dist1 > dist3){ diffDist = dist1 - dist2 - dist3; } else { diffDist = dist3 - dist2 - dist1; } } else if(dist3 > dist2){ diffDist = dist3 - dist2 - dist1; } else { diffDist = dist2 - dist1 - dist3; } return diffDist > -0.0001 && diffDist < 0.0001; } var getBezierLength = (function(){ return function(pt1,pt2,pt3,pt4){ var curveSegments = defaultCurveSegments; var k; var i, len; var ptCoord,perc,addedLength = 0; var ptDistance; var point = [],lastPoint = []; var lengthData = bezier_length_pool.newElement(); len = pt3.length; for(k=0;k lengthPos ? -1 : 1; var flag = true; while(flag){ if(lengths[initPos] <= lengthPos && lengths[initPos+1] > lengthPos){ lPerc = (lengthPos - lengths[initPos]) / (lengths[initPos+1] - lengths[initPos]); flag = false; }else{ initPos += dir; } if(initPos < 0 || initPos >= len - 1){ //FIX for TypedArrays that don't store floating point values with enough accuracy if(initPos === len - 1) { return percents[initPos]; } flag = false; } } return percents[initPos] + (percents[initPos+1] - percents[initPos])*lPerc; } } function getPointInSegment(pt1, pt2, pt3, pt4, percent, bezierData) { var t1 = getDistancePerc(percent,bezierData); var u0 = 1; var u1 = 1 - t1; var ptX = Math.round((u1*u1*u1* pt1[0] + (t1*u1*u1 + u1*t1*u1 + u1*u1*t1)* pt3[0] + (t1*t1*u1 + u1*t1*t1 + t1*u1*t1)*pt4[0] + t1*t1*t1* pt2[0])* 1000) / 1000; var ptY = Math.round((u1*u1*u1* pt1[1] + (t1*u1*u1 + u1*t1*u1 + u1*u1*t1)* pt3[1] + (t1*t1*u1 + u1*t1*t1 + t1*u1*t1)*pt4[1] + t1*t1*t1* pt2[1])* 1000) / 1000; return [ptX, ptY]; } function getSegmentArray() { } var bezier_segment_points = createTypedArray('float32', 8); function getNewSegment(pt1,pt2,pt3,pt4,startPerc,endPerc, bezierData){ startPerc = startPerc < 0 ? 0 : startPerc > 1 ? 1 : startPerc; var t0 = getDistancePerc(startPerc,bezierData); endPerc = endPerc > 1 ? 1 : endPerc; var t1 = getDistancePerc(endPerc,bezierData); var i, len = pt1.length; var u0 = 1 - t0; var u1 = 1 - t1; var u0u0u0 = u0*u0*u0; var t0u0u0_3 = t0*u0*u0*3; var t0t0u0_3 = t0*t0*u0*3; var t0t0t0 = t0*t0*t0; // var u0u0u1 = u0*u0*u1; var t0u0u1_3 = t0*u0*u1 + u0*t0*u1 + u0*u0*t1; var t0t0u1_3 = t0*t0*u1 + u0*t0*t1 + t0*u0*t1; var t0t0t1 = t0*t0*t1; // var u0u1u1 = u0*u1*u1; var t0u1u1_3 = t0*u1*u1 + u0*t1*u1 + u0*u1*t1; var t0t1u1_3 = t0*t1*u1 + u0*t1*t1 + t0*u1*t1; var t0t1t1 = t0*t1*t1; // var u1u1u1 = u1*u1*u1; var t1u1u1_3 = t1*u1*u1 + u1*t1*u1 + u1*u1*t1; var t1t1u1_3 = t1*t1*u1 + u1*t1*t1 + t1*u1*t1; var t1t1t1 = t1*t1*t1; for(i=0;i=0;i-=1){ if(arr[i].ty == 'sh'){ if(arr[i].ks.k.i){ convertPathsToAbsoluteValues(arr[i].ks.k); }else{ jLen = arr[i].ks.k.length; for(j=0;janimVersion[0]){ return true; } else if(animVersion[0] > minimum[0]){ return false; } if(minimum[1]>animVersion[1]){ return true; } else if(animVersion[1] > minimum[1]){ return false; } if(minimum[2]>animVersion[2]){ return true; } else if(animVersion[2] > minimum[2]){ return false; } } var checkText = (function(){ var minimumVersion = [4,4,14]; function updateTextLayer(textLayer){ var documentData = textLayer.t.d; textLayer.t.d = { k: [ { s:documentData, t:0 } ] }; } function iterateLayers(layers){ var i, len = layers.length; for(i=0;i=0;i-=1){ if(arr[i].ty == 'sh'){ if(arr[i].ks.k.i){ arr[i].ks.k.c = arr[i].closed; }else{ jLen = arr[i].ks.k.length; for(j=0;j 0) { shouldLoadFont = false; } if (shouldLoadFont) { var s = createTag('style'); s.setAttribute('f-forigin', fontArr[i].fOrigin); s.setAttribute('f-origin', fontArr[i].origin); s.setAttribute('f-family', fontArr[i].fFamily); s.type = "text/css"; s.innerHTML = "@font-face {" + "font-family: "+fontArr[i].fFamily+"; font-style: normal; src: url('"+fontArr[i].fPath+"');}"; defs.appendChild(s); } } else if(fontArr[i].fOrigin === 'g' || fontArr[i].origin === 1){ loadedSelector = document.querySelectorAll('link[f-forigin="g"], link[f-origin="1"]'); for (j = 0; j < loadedSelector.length; j++) { if (loadedSelector[j].href.indexOf(fontArr[i].fPath) !== -1) { // Font is already loaded shouldLoadFont = false; } } if (shouldLoadFont) { var l = createTag('link'); l.setAttribute('f-forigin', fontArr[i].fOrigin); l.setAttribute('f-origin', fontArr[i].origin); l.type = "text/css"; l.rel = "stylesheet"; l.href = fontArr[i].fPath; document.body.appendChild(l); } } else if(fontArr[i].fOrigin === 't' || fontArr[i].origin === 2){ loadedSelector = document.querySelectorAll('script[f-forigin="t"], script[f-origin="2"]'); for (j = 0; j < loadedSelector.length; j++) { if (fontArr[i].fPath === loadedSelector[j].src) { // Font is already loaded shouldLoadFont = false; } } if (shouldLoadFont) { var sc = createTag('link'); sc.setAttribute('f-forigin', fontArr[i].fOrigin); sc.setAttribute('f-origin', fontArr[i].origin); sc.setAttribute('rel','stylesheet'); sc.setAttribute('href',fontArr[i].fPath); defs.appendChild(sc); } } fontArr[i].helper = createHelper(defs,fontArr[i]); fontArr[i].cache = {}; this.fonts.push(fontArr[i]); } if (_pendingFonts === 0) { this.isLoaded = true; } else { //On some cases even if the font is loaded, it won't load correctly when measuring text on canvas. //Adding this timeout seems to fix it setTimeout(this.checkLoadedFonts.bind(this), 100); } } function addChars(chars){ if(!chars){ return; } if(!this.chars){ this.chars = []; } var i, len = chars.length; var j, jLen = this.chars.length, found; for(i=0;i= nextKeyData.t - offsetTime){ if(keyData.h){ keyData = nextKeyData; } iterationIndex = 0; break; } if ((nextKeyData.t - offsetTime) > frameNum){ iterationIndex = i; break; } if (i < len - 1){ i += 1; } else { iterationIndex = 0; flag = false; } } var k, kLen, perc, jLen, j, fnc; var nextKeyTime = nextKeyData.t - offsetTime; var keyTime = keyData.t - offsetTime; var endValue; if (keyData.to) { if (!keyData.bezierData) { keyData.bezierData = bez.buildBezierData(keyData.s, nextKeyData.s || keyData.e, keyData.to, keyData.ti); } var bezierData = keyData.bezierData; if (frameNum >= nextKeyTime || frameNum < keyTime) { var ind = frameNum >= nextKeyTime ? bezierData.points.length - 1 : 0; kLen = bezierData.points[ind].point.length; for (k = 0; k < kLen; k += 1) { newValue[k] = bezierData.points[ind].point[k]; } // caching._lastKeyframeIndex = -1; } else { if (keyData.__fnct) { fnc = keyData.__fnct; } else { fnc = BezierFactory.getBezierEasing(keyData.o.x, keyData.o.y, keyData.i.x, keyData.i.y, keyData.n).get; keyData.__fnct = fnc; } perc = fnc((frameNum - keyTime) / (nextKeyTime - keyTime)); var distanceInLine = bezierData.segmentLength*perc; var segmentPerc; var addedLength = (caching.lastFrame < frameNum && caching._lastKeyframeIndex === i) ? caching._lastAddedLength : 0; j = (caching.lastFrame < frameNum && caching._lastKeyframeIndex === i) ? caching._lastPoint : 0; flag = true; jLen = bezierData.points.length; while (flag) { addedLength += bezierData.points[j].partialLength; if (distanceInLine === 0 || perc === 0 || j === bezierData.points.length - 1) { kLen = bezierData.points[j].point.length; for (k = 0; k < kLen; k += 1) { newValue[k] = bezierData.points[j].point[k]; } break; } else if (distanceInLine >= addedLength && distanceInLine < addedLength + bezierData.points[j + 1].partialLength) { segmentPerc = (distanceInLine - addedLength) / bezierData.points[j + 1].partialLength; kLen = bezierData.points[j].point.length; for (k = 0; k < kLen; k += 1) { newValue[k] = bezierData.points[j].point[k] + (bezierData.points[j + 1].point[k] - bezierData.points[j].point[k]) * segmentPerc; } break; } if (j < jLen - 1){ j += 1; } else { flag = false; } } caching._lastPoint = j; caching._lastAddedLength = addedLength - bezierData.points[j].partialLength; caching._lastKeyframeIndex = i; } } else { var outX, outY, inX, inY, keyValue; len = keyData.s.length; endValue = nextKeyData.s || keyData.e; if (this.sh && keyData.h !== 1) { if (frameNum >= nextKeyTime) { newValue[0] = endValue[0]; newValue[1] = endValue[1]; newValue[2] = endValue[2]; } else if (frameNum <= keyTime) { newValue[0] = keyData.s[0]; newValue[1] = keyData.s[1]; newValue[2] = keyData.s[2]; } else { var quatStart = createQuaternion(keyData.s); var quatEnd = createQuaternion(endValue); var time = (frameNum - keyTime) / (nextKeyTime - keyTime); quaternionToEuler(newValue, slerp(quatStart, quatEnd, time)); } } else { for(i = 0; i < len; i += 1) { if (keyData.h !== 1) { if (frameNum >= nextKeyTime) { perc = 1; } else if(frameNum < keyTime) { perc = 0; } else { if(keyData.o.x.constructor === Array) { if (!keyData.__fnct) { keyData.__fnct = []; } if (!keyData.__fnct[i]) { outX = (typeof keyData.o.x[i] === 'undefined') ? keyData.o.x[0] : keyData.o.x[i]; outY = (typeof keyData.o.y[i] === 'undefined') ? keyData.o.y[0] : keyData.o.y[i]; inX = (typeof keyData.i.x[i] === 'undefined') ? keyData.i.x[0] : keyData.i.x[i]; inY = (typeof keyData.i.y[i] === 'undefined') ? keyData.i.y[0] : keyData.i.y[i]; fnc = BezierFactory.getBezierEasing(outX, outY, inX, inY).get; keyData.__fnct[i] = fnc; } else { fnc = keyData.__fnct[i]; } } else { if (!keyData.__fnct) { outX = keyData.o.x; outY = keyData.o.y; inX = keyData.i.x; inY = keyData.i.y; fnc = BezierFactory.getBezierEasing(outX, outY, inX, inY).get; keyData.__fnct = fnc; } else { fnc = keyData.__fnct; } } perc = fnc((frameNum - keyTime) / (nextKeyTime - keyTime )); } } endValue = nextKeyData.s || keyData.e; keyValue = keyData.h === 1 ? keyData.s[i] : keyData.s[i] + (endValue[i] - keyData.s[i]) * perc; if (len === 1) { newValue = keyValue; } else { newValue[i] = keyValue; } } } } caching.lastIndex = iterationIndex; return newValue; } //based on @Toji's https://github.com/toji/gl-matrix/ function slerp(a, b, t) { var out = []; var ax = a[0], ay = a[1], az = a[2], aw = a[3], bx = b[0], by = b[1], bz = b[2], bw = b[3] var omega, cosom, sinom, scale0, scale1; cosom = ax * bx + ay * by + az * bz + aw * bw; if (cosom < 0.0) { cosom = -cosom; bx = -bx; by = -by; bz = -bz; bw = -bw; } if ((1.0 - cosom) > 0.000001) { omega = Math.acos(cosom); sinom = Math.sin(omega); scale0 = Math.sin((1.0 - t) * omega) / sinom; scale1 = Math.sin(t * omega) / sinom; } else { scale0 = 1.0 - t; scale1 = t; } out[0] = scale0 * ax + scale1 * bx; out[1] = scale0 * ay + scale1 * by; out[2] = scale0 * az + scale1 * bz; out[3] = scale0 * aw + scale1 * bw; return out; } function quaternionToEuler(out, quat) { var qx = quat[0]; var qy = quat[1]; var qz = quat[2]; var qw = quat[3]; var heading = Math.atan2(2*qy*qw-2*qx*qz , 1 - 2*qy*qy - 2*qz*qz) var attitude = Math.asin(2*qx*qy + 2*qz*qw) var bank = Math.atan2(2*qx*qw-2*qy*qz , 1 - 2*qx*qx - 2*qz*qz); out[0] = heading/degToRads; out[1] = attitude/degToRads; out[2] = bank/degToRads; } function createQuaternion(values) { var heading = values[0] * degToRads; var attitude = values[1] * degToRads; var bank = values[2] * degToRads; var c1 = Math.cos(heading / 2); var c2 = Math.cos(attitude / 2); var c3 = Math.cos(bank / 2); var s1 = Math.sin(heading / 2); var s2 = Math.sin(attitude / 2); var s3 = Math.sin(bank / 2); var w = c1 * c2 * c3 - s1 * s2 * s3; var x = s1 * s2 * c3 + c1 * c2 * s3; var y = s1 * c2 * c3 + c1 * s2 * s3; var z = c1 * s2 * c3 - s1 * c2 * s3; return [x,y,z,w]; } function getValueAtCurrentTime(){ var frameNum = this.comp.renderedFrame - this.offsetTime; var initTime = this.keyframes[0].t - this.offsetTime; var endTime = this.keyframes[this.keyframes.length- 1].t-this.offsetTime; if(!(frameNum === this._caching.lastFrame || (this._caching.lastFrame !== initFrame && ((this._caching.lastFrame >= endTime && frameNum >= endTime) || (this._caching.lastFrame < initTime && frameNum < initTime))))){ if(this._caching.lastFrame >= frameNum) { this._caching._lastKeyframeIndex = -1; this._caching.lastIndex = 0; } var renderResult = this.interpolateValue(frameNum, this._caching); this.pv = renderResult; } this._caching.lastFrame = frameNum; return this.pv; } function setVValue(val) { var multipliedValue; if(this.propType === 'unidimensional') { multipliedValue = val * this.mult; if(math_abs(this.v - multipliedValue) > 0.00001) { this.v = multipliedValue; this._mdf = true; } } else { var i = 0, len = this.v.length; while (i < len) { multipliedValue = val[i] * this.mult; if (math_abs(this.v[i] - multipliedValue) > 0.00001) { this.v[i] = multipliedValue; this._mdf = true; } i += 1; } } } function processEffectsSequence() { if(this.elem.globalData.frameId === this.frameId || !this.effectsSequence.length) { return; } if(this.lock) { this.setVValue(this.pv); return; } this.lock = true; this._mdf = this._isFirstFrame; var multipliedValue; var i, len = this.effectsSequence.length; var finalValue = this.kf ? this.pv : this.data.k; for(i = 0; i < len; i += 1) { finalValue = this.effectsSequence[i](finalValue); } this.setVValue(finalValue); this._isFirstFrame = false; this.lock = false; this.frameId = this.elem.globalData.frameId; } function addEffect(effectFunction) { this.effectsSequence.push(effectFunction); this.container.addDynamicProperty(this); } function ValueProperty(elem, data, mult, container){ this.propType = 'unidimensional'; this.mult = mult || 1; this.data = data; this.v = mult ? data.k * mult : data.k; this.pv = data.k; this._mdf = false; this.elem = elem; this.container = container; this.comp = elem.comp; this.k = false; this.kf = false; this.vel = 0; this.effectsSequence = []; this._isFirstFrame = true; this.getValue = processEffectsSequence; this.setVValue = setVValue; this.addEffect = addEffect; } function MultiDimensionalProperty(elem, data, mult, container) { this.propType = 'multidimensional'; this.mult = mult || 1; this.data = data; this._mdf = false; this.elem = elem; this.container = container; this.comp = elem.comp; this.k = false; this.kf = false; this.frameId = -1; var i, len = data.k.length; this.v = createTypedArray('float32', len); this.pv = createTypedArray('float32', len); var arr = createTypedArray('float32', len); this.vel = createTypedArray('float32', len); for (i = 0; i < len; i += 1) { this.v[i] = data.k[i] * this.mult; this.pv[i] = data.k[i]; } this._isFirstFrame = true; this.effectsSequence = []; this.getValue = processEffectsSequence; this.setVValue = setVValue; this.addEffect = addEffect; } function KeyframedValueProperty(elem, data, mult, container) { this.propType = 'unidimensional'; this.keyframes = data.k; this.offsetTime = elem.data.st; this.frameId = -1; this._caching = {lastFrame: initFrame, lastIndex: 0, value: 0, _lastKeyframeIndex: -1}; this.k = true; this.kf = true; this.data = data; this.mult = mult || 1; this.elem = elem; this.container = container; this.comp = elem.comp; this.v = initFrame; this.pv = initFrame; this._isFirstFrame = true; this.getValue = processEffectsSequence; this.setVValue = setVValue; this.interpolateValue = interpolateValue; this.effectsSequence = [getValueAtCurrentTime.bind(this)]; this.addEffect = addEffect; } function KeyframedMultidimensionalProperty(elem, data, mult, container){ this.propType = 'multidimensional'; var i, len = data.k.length; var s, e,to,ti; for (i = 0; i < len - 1; i += 1) { if (data.k[i].to && data.k[i].s && data.k[i].e) { s = data.k[i].s; e = data.k[i].e; to = data.k[i].to; ti = data.k[i].ti; if((s.length === 2 && !(s[0] === e[0] && s[1] === e[1]) && bez.pointOnLine2D(s[0],s[1],e[0],e[1],s[0] + to[0],s[1] + to[1]) && bez.pointOnLine2D(s[0],s[1],e[0],e[1],e[0] + ti[0],e[1] + ti[1])) || (s.length === 3 && !(s[0] === e[0] && s[1] === e[1] && s[2] === e[2]) && bez.pointOnLine3D(s[0],s[1],s[2],e[0],e[1],e[2],s[0] + to[0],s[1] + to[1],s[2] + to[2]) && bez.pointOnLine3D(s[0],s[1],s[2],e[0],e[1],e[2],e[0] + ti[0],e[1] + ti[1],e[2] + ti[2]))){ data.k[i].to = null; data.k[i].ti = null; } if(s[0] === e[0] && s[1] === e[1] && to[0] === 0 && to[1] === 0 && ti[0] === 0 && ti[1] === 0) { if(s.length === 2 || (s[2] === e[2] && to[2] === 0 && ti[2] === 0)) { data.k[i].to = null; data.k[i].ti = null; } } } } this.effectsSequence = [getValueAtCurrentTime.bind(this)]; this.keyframes = data.k; this.offsetTime = elem.data.st; this.k = true; this.kf = true; this._isFirstFrame = true; this.mult = mult || 1; this.elem = elem; this.container = container; this.comp = elem.comp; this.getValue = processEffectsSequence; this.setVValue = setVValue; this.interpolateValue = interpolateValue; this.frameId = -1; var arrLen = data.k[0].s.length; this.v = createTypedArray('float32', arrLen); this.pv = createTypedArray('float32', arrLen); for (i = 0; i < arrLen; i += 1) { this.v[i] = initFrame; this.pv[i] = initFrame; } this._caching={lastFrame:initFrame,lastIndex:0,value:createTypedArray('float32', arrLen)}; this.addEffect = addEffect; } function getProp(elem,data,type, mult, container) { var p; if(!data.k.length){ p = new ValueProperty(elem,data, mult, container); }else if(typeof(data.k[0]) === 'number'){ p = new MultiDimensionalProperty(elem,data, mult, container); }else{ switch(type){ case 0: p = new KeyframedValueProperty(elem,data,mult, container); break; case 1: p = new KeyframedMultidimensionalProperty(elem,data,mult, container); break; } } if(p.effectsSequence.length){ container.addDynamicProperty(p); } return p; } var ob = { getProp: getProp }; return ob; }()); var TransformPropertyFactory = (function() { function applyToMatrix(mat) { var _mdf = this._mdf; this.iterateDynamicProperties(); this._mdf = this._mdf || _mdf; if (this.a) { mat.translate(-this.a.v[0], -this.a.v[1], this.a.v[2]); } if (this.s) { mat.scale(this.s.v[0], this.s.v[1], this.s.v[2]); } if (this.sk) { mat.skewFromAxis(-this.sk.v, this.sa.v); } if (this.r) { mat.rotate(-this.r.v); } else { mat.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]); } if (this.data.p.s) { if (this.data.p.z) { mat.translate(this.px.v, this.py.v, -this.pz.v); } else { mat.translate(this.px.v, this.py.v, 0); } } else { mat.translate(this.p.v[0], this.p.v[1], -this.p.v[2]); } } function processKeys(forceRender){ if (this.elem.globalData.frameId === this.frameId) { return; } if(this._isDirty) { this.precalculateMatrix(); this._isDirty = false; } this.iterateDynamicProperties(); if (this._mdf || forceRender) { this.v.cloneFromProps(this.pre.props); if (this.appliedTransformations < 1) { this.v.translate(-this.a.v[0], -this.a.v[1], this.a.v[2]); } if(this.appliedTransformations < 2) { this.v.scale(this.s.v[0], this.s.v[1], this.s.v[2]); } if (this.sk && this.appliedTransformations < 3) { this.v.skewFromAxis(-this.sk.v, this.sa.v); } if (this.r && this.appliedTransformations < 4) { this.v.rotate(-this.r.v); } else if (!this.r && this.appliedTransformations < 4){ this.v.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]); } if (this.autoOriented) { var v1,v2, frameRate = this.elem.globalData.frameRate; if(this.p && this.p.keyframes && this.p.getValueAtTime) { if (this.p._caching.lastFrame+this.p.offsetTime <= this.p.keyframes[0].t) { v1 = this.p.getValueAtTime((this.p.keyframes[0].t + 0.01) / frameRate,0); v2 = this.p.getValueAtTime(this.p.keyframes[0].t / frameRate, 0); } else if(this.p._caching.lastFrame+this.p.offsetTime >= this.p.keyframes[this.p.keyframes.length - 1].t) { v1 = this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length - 1].t / frameRate), 0); v2 = this.p.getValueAtTime((this.p.keyframes[this.p.keyframes.length - 1].t - 0.01) / frameRate, 0); } else { v1 = this.p.pv; v2 = this.p.getValueAtTime((this.p._caching.lastFrame+this.p.offsetTime - 0.01) / frameRate, this.p.offsetTime); } } else if(this.px && this.px.keyframes && this.py.keyframes && this.px.getValueAtTime && this.py.getValueAtTime) { v1 = []; v2 = []; var px = this.px, py = this.py, frameRate; if (px._caching.lastFrame+px.offsetTime <= px.keyframes[0].t) { v1[0] = px.getValueAtTime((px.keyframes[0].t + 0.01) / frameRate,0); v1[1] = py.getValueAtTime((py.keyframes[0].t + 0.01) / frameRate,0); v2[0] = px.getValueAtTime((px.keyframes[0].t) / frameRate,0); v2[1] = py.getValueAtTime((py.keyframes[0].t) / frameRate,0); } else if(px._caching.lastFrame+px.offsetTime >= px.keyframes[px.keyframes.length - 1].t) { v1[0] = px.getValueAtTime((px.keyframes[px.keyframes.length - 1].t / frameRate),0); v1[1] = py.getValueAtTime((py.keyframes[py.keyframes.length - 1].t / frameRate),0); v2[0] = px.getValueAtTime((px.keyframes[px.keyframes.length - 1].t - 0.01) / frameRate,0); v2[1] = py.getValueAtTime((py.keyframes[py.keyframes.length - 1].t - 0.01) / frameRate,0); } else { v1 = [px.pv, py.pv]; v2[0] = px.getValueAtTime((px._caching.lastFrame+px.offsetTime - 0.01) / frameRate,px.offsetTime); v2[1] = py.getValueAtTime((py._caching.lastFrame+py.offsetTime - 0.01) / frameRate,py.offsetTime); } } this.v.rotate(-Math.atan2(v1[1] - v2[1], v1[0] - v2[0])); } if(this.data.p && this.data.p.s){ if(this.data.p.z) { this.v.translate(this.px.v, this.py.v, -this.pz.v); } else { this.v.translate(this.px.v, this.py.v, 0); } }else{ this.v.translate(this.p.v[0],this.p.v[1],-this.p.v[2]); } } this.frameId = this.elem.globalData.frameId; } function precalculateMatrix() { if(!this.a.k) { this.pre.translate(-this.a.v[0], -this.a.v[1], this.a.v[2]); this.appliedTransformations = 1; } else { return; } if(!this.s.effectsSequence.length) { this.pre.scale(this.s.v[0], this.s.v[1], this.s.v[2]); this.appliedTransformations = 2; } else { return; } if(this.sk) { if(!this.sk.effectsSequence.length && !this.sa.effectsSequence.length) { this.pre.skewFromAxis(-this.sk.v, this.sa.v); this.appliedTransformations = 3; } else { return; } } if (this.r) { if(!this.r.effectsSequence.length) { this.pre.rotate(-this.r.v); this.appliedTransformations = 4; } else { return; } } else if(!this.rz.effectsSequence.length && !this.ry.effectsSequence.length && !this.rx.effectsSequence.length && !this.or.effectsSequence.length) { this.pre.rotateZ(-this.rz.v).rotateY(this.ry.v).rotateX(this.rx.v).rotateZ(-this.or.v[2]).rotateY(this.or.v[1]).rotateX(this.or.v[0]); this.appliedTransformations = 4; } } function autoOrient(){ // //var prevP = this.getValueAtTime(); } function addDynamicProperty(prop) { this._addDynamicProperty(prop); this.elem.addDynamicProperty(prop); this._isDirty = true; } function TransformProperty(elem,data,container){ this.elem = elem; this.frameId = -1; this.propType = 'transform'; this.data = data; this.v = new Matrix(); //Precalculated matrix with non animated properties this.pre = new Matrix(); this.appliedTransformations = 0; this.initDynamicPropertyContainer(container || elem); if(data.p && data.p.s){ this.px = PropertyFactory.getProp(elem,data.p.x,0,0,this); this.py = PropertyFactory.getProp(elem,data.p.y,0,0,this); if(data.p.z){ this.pz = PropertyFactory.getProp(elem,data.p.z,0,0,this); } }else{ this.p = PropertyFactory.getProp(elem,data.p || {k:[0,0,0]},1,0,this); } if(data.rx) { this.rx = PropertyFactory.getProp(elem, data.rx, 0, degToRads, this); this.ry = PropertyFactory.getProp(elem, data.ry, 0, degToRads, this); this.rz = PropertyFactory.getProp(elem, data.rz, 0, degToRads, this); if(data.or.k[0].ti) { var i, len = data.or.k.length; for(i=0;i= this._maxLength) { this.doubleArrayLength(); } switch(type){ case 'v': arr = this.v; break; case 'i': arr = this.i; break; case 'o': arr = this.o; break; } if(!arr[pos] || (arr[pos] && !replace)){ arr[pos] = point_pool.newElement(); } arr[pos][0] = x; arr[pos][1] = y; }; ShapePath.prototype.setTripleAt = function(vX,vY,oX,oY,iX,iY,pos, replace) { this.setXYAt(vX,vY,'v',pos, replace); this.setXYAt(oX,oY,'o',pos, replace); this.setXYAt(iX,iY,'i',pos, replace); }; ShapePath.prototype.reverse = function() { var newPath = new ShapePath(); newPath.setPathData(this.c, this._length); var vertices = this.v, outPoints = this.o, inPoints = this.i; var init = 0; if (this.c) { newPath.setTripleAt(vertices[0][0], vertices[0][1], inPoints[0][0], inPoints[0][1], outPoints[0][0], outPoints[0][1], 0, false); init = 1; } var cnt = this._length - 1; var len = this._length; var i; for (i = init; i < len; i += 1) { newPath.setTripleAt(vertices[cnt][0], vertices[cnt][1], inPoints[cnt][0], inPoints[cnt][1], outPoints[cnt][0], outPoints[cnt][1], i, false); cnt -= 1; } return newPath; }; var ShapePropertyFactory = (function(){ var initFrame = -999999; function interpolateShape(frameNum, previousValue, caching) { var iterationIndex = caching.lastIndex; var keyPropS,keyPropE,isHold, j, k, jLen, kLen, perc, vertexValue; var kf = this.keyframes; if(frameNum < kf[0].t-this.offsetTime){ keyPropS = kf[0].s[0]; isHold = true; iterationIndex = 0; }else if(frameNum >= kf[kf.length - 1].t-this.offsetTime){ keyPropS = kf[kf.length - 1].s ? kf[kf.length - 1].s[0] : kf[kf.length - 2].e[0]; /*if(kf[kf.length - 1].s){ keyPropS = kf[kf.length - 1].s[0]; }else{ keyPropS = kf[kf.length - 2].e[0]; }*/ isHold = true; }else{ var i = iterationIndex; var len = kf.length- 1,flag = true,keyData,nextKeyData; while(flag){ keyData = kf[i]; nextKeyData = kf[i+1]; if((nextKeyData.t - this.offsetTime) > frameNum){ break; } if(i < len - 1){ i += 1; }else{ flag = false; } } isHold = keyData.h === 1; iterationIndex = i; if(!isHold){ if(frameNum >= nextKeyData.t-this.offsetTime){ perc = 1; }else if(frameNum < keyData.t-this.offsetTime){ perc = 0; }else{ var fnc; if(keyData.__fnct){ fnc = keyData.__fnct; }else{ fnc = BezierFactory.getBezierEasing(keyData.o.x,keyData.o.y,keyData.i.x,keyData.i.y).get; keyData.__fnct = fnc; } perc = fnc((frameNum-(keyData.t-this.offsetTime))/((nextKeyData.t-this.offsetTime)-(keyData.t-this.offsetTime))); } keyPropE = nextKeyData.s ? nextKeyData.s[0] : keyData.e[0]; } keyPropS = keyData.s[0]; } jLen = previousValue._length; kLen = keyPropS.i[0].length; caching.lastIndex = iterationIndex; for(j=0;j endTime && frameNum > endTime)))){ //// this._caching.lastIndex = lastFrame < frameNum ? this._caching.lastIndex : 0; this.interpolateShape(frameNum, this.pv, this._caching); //// } this._caching.lastFrame = frameNum; return this.pv; } function resetShape(){ this.paths = this.localShapeCollection; } function shapesEqual(shape1, shape2) { if(shape1._length !== shape2._length || shape1.c !== shape2.c){ return false; } var i, len = shape1._length; for(i = 0; i < len; i += 1) { if(shape1.v[i][0] !== shape2.v[i][0] || shape1.v[i][1] !== shape2.v[i][1] || shape1.o[i][0] !== shape2.o[i][0] || shape1.o[i][1] !== shape2.o[i][1] || shape1.i[i][0] !== shape2.i[i][0] || shape1.i[i][1] !== shape2.i[i][1]) { return false; } } return true; } function setVValue(newPath) { if(!shapesEqual(this.v, newPath)) { this.v = shape_pool.clone(newPath); this.localShapeCollection.releaseShapes(); this.localShapeCollection.addShape(this.v); this._mdf = true; this.paths = this.localShapeCollection; } } function processEffectsSequence() { if(this.elem.globalData.frameId === this.frameId || !this.effectsSequence.length) { return; } if(this.lock) { this.setVValue(this.pv); return; } this.lock = true; this._mdf = false; var finalValue = this.kf ? this.pv : this.data.ks ? this.data.ks.k : this.data.pt.k; var i, len = this.effectsSequence.length; for(i = 0; i < len; i += 1) { finalValue = this.effectsSequence[i](finalValue); } this.setVValue(finalValue); this.lock = false; this.frameId = this.elem.globalData.frameId; }; function ShapeProperty(elem, data, type){ this.propType = 'shape'; this.comp = elem.comp; this.container = elem; this.elem = elem; this.data = data; this.k = false; this.kf = false; this._mdf = false; var pathData = type === 3 ? data.pt.k : data.ks.k; this.v = shape_pool.clone(pathData); this.pv = shape_pool.clone(this.v); this.localShapeCollection = shapeCollection_pool.newShapeCollection(); this.paths = this.localShapeCollection; this.paths.addShape(this.v); this.reset = resetShape; this.effectsSequence = []; } function addEffect(effectFunction) { this.effectsSequence.push(effectFunction); this.container.addDynamicProperty(this); } ShapeProperty.prototype.interpolateShape = interpolateShape; ShapeProperty.prototype.getValue = processEffectsSequence; ShapeProperty.prototype.setVValue = setVValue; ShapeProperty.prototype.addEffect = addEffect; function KeyframedShapeProperty(elem,data,type){ this.propType = 'shape'; this.comp = elem.comp; this.elem = elem; this.container = elem; this.offsetTime = elem.data.st; this.keyframes = type === 3 ? data.pt.k : data.ks.k; this.k = true; this.kf = true; var i, len = this.keyframes[0].s[0].i.length; var jLen = this.keyframes[0].s[0].i[0].length; this.v = shape_pool.newElement(); this.v.setPathData(this.keyframes[0].s[0].c, len); this.pv = shape_pool.clone(this.v); this.localShapeCollection = shapeCollection_pool.newShapeCollection(); this.paths = this.localShapeCollection; this.paths.addShape(this.v); this.lastFrame = initFrame; this.reset = resetShape; this._caching = {lastFrame: initFrame, lastIndex: 0}; this.effectsSequence = [interpolateShapeCurrentTime.bind(this)]; } KeyframedShapeProperty.prototype.getValue = processEffectsSequence; KeyframedShapeProperty.prototype.interpolateShape = interpolateShape; KeyframedShapeProperty.prototype.setVValue = setVValue; KeyframedShapeProperty.prototype.addEffect = addEffect; var EllShapeProperty = (function(){ var cPoint = roundCorner; function EllShapeProperty(elem,data) { /*this.v = { v: createSizedArray(4), i: createSizedArray(4), o: createSizedArray(4), c: true };*/ this.v = shape_pool.newElement(); this.v.setPathData(true, 4); this.localShapeCollection = shapeCollection_pool.newShapeCollection(); this.paths = this.localShapeCollection; this.localShapeCollection.addShape(this.v); this.d = data.d; this.elem = elem; this.comp = elem.comp; this.frameId = -1; this.initDynamicPropertyContainer(elem); this.p = PropertyFactory.getProp(elem,data.p,1,0,this); this.s = PropertyFactory.getProp(elem,data.s,1,0,this); if(this.dynamicProperties.length){ this.k = true; }else{ this.k = false; this.convertEllToPath(); } }; EllShapeProperty.prototype = { reset: resetShape, getValue: function (){ if(this.elem.globalData.frameId === this.frameId){ return; } this.frameId = this.elem.globalData.frameId; this.iterateDynamicProperties(); if(this._mdf){ this.convertEllToPath(); } }, convertEllToPath: function() { var p0 = this.p.v[0], p1 = this.p.v[1], s0 = this.s.v[0]/2, s1 = this.s.v[1]/2; var _cw = this.d !== 3; var _v = this.v; _v.v[0][0] = p0; _v.v[0][1] = p1 - s1; _v.v[1][0] = _cw ? p0 + s0 : p0 - s0; _v.v[1][1] = p1; _v.v[2][0] = p0; _v.v[2][1] = p1 + s1; _v.v[3][0] = _cw ? p0 - s0 : p0 + s0; _v.v[3][1] = p1; _v.i[0][0] = _cw ? p0 - s0 * cPoint : p0 + s0 * cPoint; _v.i[0][1] = p1 - s1; _v.i[1][0] = _cw ? p0 + s0 : p0 - s0; _v.i[1][1] = p1 - s1 * cPoint; _v.i[2][0] = _cw ? p0 + s0 * cPoint : p0 - s0 * cPoint; _v.i[2][1] = p1 + s1; _v.i[3][0] = _cw ? p0 - s0 : p0 + s0; _v.i[3][1] = p1 + s1 * cPoint; _v.o[0][0] = _cw ? p0 + s0 * cPoint : p0 - s0 * cPoint; _v.o[0][1] = p1 - s1; _v.o[1][0] = _cw ? p0 + s0 : p0 - s0; _v.o[1][1] = p1 + s1 * cPoint; _v.o[2][0] = _cw ? p0 - s0 * cPoint : p0 + s0 * cPoint; _v.o[2][1] = p1 + s1; _v.o[3][0] = _cw ? p0 - s0 : p0 + s0; _v.o[3][1] = p1 - s1 * cPoint; } } extendPrototype([DynamicPropertyContainer], EllShapeProperty); return EllShapeProperty; }()); var StarShapeProperty = (function() { function StarShapeProperty(elem,data) { this.v = shape_pool.newElement(); this.v.setPathData(true, 0); this.elem = elem; this.comp = elem.comp; this.data = data; this.frameId = -1; this.d = data.d; this.initDynamicPropertyContainer(elem); if(data.sy === 1){ this.ir = PropertyFactory.getProp(elem,data.ir,0,0,this); this.is = PropertyFactory.getProp(elem,data.is,0,0.01,this); this.convertToPath = this.convertStarToPath; } else { this.convertToPath = this.convertPolygonToPath; } this.pt = PropertyFactory.getProp(elem,data.pt,0,0,this); this.p = PropertyFactory.getProp(elem,data.p,1,0,this); this.r = PropertyFactory.getProp(elem,data.r,0,degToRads,this); this.or = PropertyFactory.getProp(elem,data.or,0,0,this); this.os = PropertyFactory.getProp(elem,data.os,0,0.01,this); this.localShapeCollection = shapeCollection_pool.newShapeCollection(); this.localShapeCollection.addShape(this.v); this.paths = this.localShapeCollection; if(this.dynamicProperties.length){ this.k = true; }else{ this.k = false; this.convertToPath(); } }; StarShapeProperty.prototype = { reset: resetShape, getValue: function() { if(this.elem.globalData.frameId === this.frameId){ return; } this.frameId = this.elem.globalData.frameId; this.iterateDynamicProperties(); if(this._mdf){ this.convertToPath(); } }, convertStarToPath: function() { var numPts = Math.floor(this.pt.v)*2; var angle = Math.PI*2/numPts; /*this.v.v.length = numPts; this.v.i.length = numPts; this.v.o.length = numPts;*/ var longFlag = true; var longRad = this.or.v; var shortRad = this.ir.v; var longRound = this.os.v; var shortRound = this.is.v; var longPerimSegment = 2*Math.PI*longRad/(numPts*2); var shortPerimSegment = 2*Math.PI*shortRad/(numPts*2); var i, rad,roundness,perimSegment, currentAng = -Math.PI/ 2; currentAng += this.r.v; var dir = this.data.d === 3 ? -1 : 1; this.v._length = 0; for(i=0;i= 1) { segments.push({ s: s - 1, e: e - 1 }); } else { segments.push({ s: s, e: 1 }); segments.push({ s: 0, e: e - 1 }); } var shapeSegments = []; var i, len = segments.length, segmentOb; for (i = 0; i < len; i += 1) { segmentOb = segments[i]; if (segmentOb.e * totalModifierLength < addedLength || segmentOb.s * totalModifierLength > addedLength + shapeLength) { } else { var shapeS, shapeE; if (segmentOb.s * totalModifierLength <= addedLength) { shapeS = 0; } else { shapeS = (segmentOb.s * totalModifierLength - addedLength) / shapeLength; } if(segmentOb.e * totalModifierLength >= addedLength + shapeLength) { shapeE = 1; } else { shapeE = ((segmentOb.e * totalModifierLength - addedLength) / shapeLength); } shapeSegments.push([shapeS, shapeE]); } } if (!shapeSegments.length) { shapeSegments.push([0, 0]); } return shapeSegments; }; TrimModifier.prototype.releasePathsData = function(pathsData) { var i, len = pathsData.length; for (i = 0; i < len; i += 1) { segments_length_pool.release(pathsData[i]); } pathsData.length = 0; return pathsData; }; TrimModifier.prototype.processShapes = function(_isFirstFrame) { var s, e; if (this._mdf || _isFirstFrame) { var o = (this.o.v % 360) / 360; if (o < 0) { o += 1; } s = (this.s.v > 1 ? 1 : this.s.v < 0 ? 0 : this.s.v) + o; e = (this.e.v > 1 ? 1 : this.e.v < 0 ? 0 : this.e.v) + o; if (s === e) { } if (s > e) { var _s = s; s = e; e = _s; } s = Math.round(s * 10000) * 0.0001; e = Math.round(e * 10000) * 0.0001; this.sValue = s; this.eValue = e; } else { s = this.sValue; e = this.eValue; } var shapePaths; var i, len = this.shapes.length, j, jLen; var pathsData, pathData, totalShapeLength, totalModifierLength = 0; if (e === s) { for (i = 0; i < len; i += 1) { this.shapes[i].localShapeCollection.releaseShapes(); this.shapes[i].shape._mdf = true; this.shapes[i].shape.paths = this.shapes[i].localShapeCollection; } } else if (!((e === 1 && s === 0) || (e===0 && s === 1))){ var segments = [], shapeData, localShapeCollection; for (i = 0; i < len; i += 1) { shapeData = this.shapes[i]; // if shape hasn't changed and trim properties haven't changed, cached previous path can be used if (!shapeData.shape._mdf && !this._mdf && !_isFirstFrame && this.m !== 2) { shapeData.shape.paths = shapeData.localShapeCollection; } else { shapePaths = shapeData.shape.paths; jLen = shapePaths._length; totalShapeLength = 0; if (!shapeData.shape._mdf && shapeData.pathsData.length) { totalShapeLength = shapeData.totalShapeLength; } else { pathsData = this.releasePathsData(shapeData.pathsData); for (j = 0; j < jLen; j += 1) { pathData = bez.getSegmentsLength(shapePaths.shapes[j]); pathsData.push(pathData); totalShapeLength += pathData.totalLength; } shapeData.totalShapeLength = totalShapeLength; shapeData.pathsData = pathsData; } totalModifierLength += totalShapeLength; shapeData.shape._mdf = true; } } var shapeS = s, shapeE = e, addedLength = 0, edges; for (i = len - 1; i >= 0; i -= 1) { shapeData = this.shapes[i]; if (shapeData.shape._mdf) { localShapeCollection = shapeData.localShapeCollection; localShapeCollection.releaseShapes(); //if m === 2 means paths are trimmed individually so edges need to be found for this specific shape relative to whoel group if (this.m === 2 && len > 1) { edges = this.calculateShapeEdges(s, e, shapeData.totalShapeLength, addedLength, totalModifierLength); addedLength += shapeData.totalShapeLength; } else { edges = [[shapeS, shapeE]]; } jLen = edges.length; for (j = 0; j < jLen; j += 1) { shapeS = edges[j][0]; shapeE = edges[j][1]; segments.length = 0; if (shapeE <= 1) { segments.push({ s:shapeData.totalShapeLength * shapeS, e:shapeData.totalShapeLength * shapeE }); } else if (shapeS >= 1) { segments.push({ s:shapeData.totalShapeLength * (shapeS - 1), e:shapeData.totalShapeLength * (shapeE - 1) }); } else { segments.push({ s:shapeData.totalShapeLength * shapeS, e:shapeData.totalShapeLength }); segments.push({ s:0, e:shapeData.totalShapeLength * (shapeE - 1) }); } var newShapesData = this.addShapes(shapeData,segments[0]); if (segments[0].s !== segments[0].e) { if (segments.length > 1) { var lastShapeInCollection = shapeData.shape.paths.shapes[shapeData.shape.paths._length - 1]; if (lastShapeInCollection.c) { var lastShape = newShapesData.pop(); this.addPaths(newShapesData, localShapeCollection); newShapesData = this.addShapes(shapeData, segments[1], lastShape); } else { this.addPaths(newShapesData, localShapeCollection); newShapesData = this.addShapes(shapeData, segments[1]); } } this.addPaths(newShapesData, localShapeCollection); } } shapeData.shape.paths = localShapeCollection; } } } else if (this._mdf) { for (i = 0; i < len; i += 1) { //Releasign Trim Cached paths data when no trim applied in case shapes are modified inbetween. //Don't remove this even if it's losing cached info. this.shapes[i].pathsData.length = 0; this.shapes[i].shape._mdf = true; } } }; TrimModifier.prototype.addPaths = function(newPaths, localShapeCollection) { var i, len = newPaths.length; for (i = 0; i < len; i += 1) { localShapeCollection.addShape(newPaths[i]); } }; TrimModifier.prototype.addSegment = function(pt1, pt2, pt3, pt4, shapePath, pos, newShape) { shapePath.setXYAt(pt2[0], pt2[1], 'o', pos); shapePath.setXYAt(pt3[0], pt3[1], 'i', pos + 1); if(newShape){ shapePath.setXYAt(pt1[0], pt1[1], 'v', pos); } shapePath.setXYAt(pt4[0], pt4[1], 'v', pos + 1); }; TrimModifier.prototype.addSegmentFromArray = function(points, shapePath, pos, newShape) { shapePath.setXYAt(points[1], points[5], 'o', pos); shapePath.setXYAt(points[2], points[6], 'i', pos + 1); if(newShape){ shapePath.setXYAt(points[0], points[4], 'v', pos); } shapePath.setXYAt(points[3], points[7], 'v', pos + 1); }; TrimModifier.prototype.addShapes = function(shapeData, shapeSegment, shapePath) { var pathsData = shapeData.pathsData; var shapePaths = shapeData.shape.paths.shapes; var i, len = shapeData.shape.paths._length, j, jLen; var addedLength = 0; var currentLengthData,segmentCount; var lengths; var segment; var shapes = []; var initPos; var newShape = true; if (!shapePath) { shapePath = shape_pool.newElement(); segmentCount = 0; initPos = 0; } else { segmentCount = shapePath._length; initPos = shapePath._length; } shapes.push(shapePath); for (i = 0; i < len; i += 1) { lengths = pathsData[i].lengths; shapePath.c = shapePaths[i].c; jLen = shapePaths[i].c ? lengths.length : lengths.length + 1; for (j = 1; j < jLen; j +=1) { currentLengthData = lengths[j-1]; if (addedLength + currentLengthData.addedLength < shapeSegment.s) { addedLength += currentLengthData.addedLength; shapePath.c = false; } else if(addedLength > shapeSegment.e) { shapePath.c = false; break; } else { if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + currentLengthData.addedLength) { this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[j], shapePaths[i].v[j], shapePath, segmentCount, newShape); newShape = false; } else { segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[j], shapePaths[i].o[j - 1], shapePaths[i].i[j], (shapeSegment.s - addedLength)/currentLengthData.addedLength,(shapeSegment.e - addedLength)/currentLengthData.addedLength, lengths[j-1]); this.addSegmentFromArray(segment, shapePath, segmentCount, newShape); // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape); newShape = false; shapePath.c = false; } addedLength += currentLengthData.addedLength; segmentCount += 1; } } if (shapePaths[i].c && lengths.length) { currentLengthData = lengths[j - 1]; if (addedLength <= shapeSegment.e) { var segmentLength = lengths[j - 1].addedLength; if (shapeSegment.s <= addedLength && shapeSegment.e >= addedLength + segmentLength) { this.addSegment(shapePaths[i].v[j - 1], shapePaths[i].o[j - 1], shapePaths[i].i[0], shapePaths[i].v[0], shapePath, segmentCount, newShape); newShape = false; } else { segment = bez.getNewSegment(shapePaths[i].v[j - 1], shapePaths[i].v[0], shapePaths[i].o[j - 1], shapePaths[i].i[0], (shapeSegment.s - addedLength) / segmentLength, (shapeSegment.e - addedLength) / segmentLength, lengths[j - 1]); this.addSegmentFromArray(segment, shapePath, segmentCount, newShape); // this.addSegment(segment.pt1, segment.pt3, segment.pt4, segment.pt2, shapePath, segmentCount, newShape); newShape = false; shapePath.c = false; } } else { shapePath.c = false; } addedLength += currentLengthData.addedLength; segmentCount += 1; } if (shapePath._length) { shapePath.setXYAt(shapePath.v[initPos][0], shapePath.v[initPos][1], 'i', initPos); shapePath.setXYAt(shapePath.v[shapePath._length - 1][0], shapePath.v[shapePath._length - 1][1],'o', shapePath._length - 1); } if (addedLength > shapeSegment.e) { break; } if (i < len - 1) { shapePath = shape_pool.newElement(); newShape = true; shapes.push(shapePath); segmentCount = 0; } } return shapes; }; ShapeModifiers.registerModifier('tm', TrimModifier); function RoundCornersModifier(){} extendPrototype([ShapeModifier],RoundCornersModifier); RoundCornersModifier.prototype.initModifierProperties = function(elem,data){ this.getValue = this.processKeys; this.rd = PropertyFactory.getProp(elem,data.r,0,null,this); this._isAnimated = !!this.rd.effectsSequence.length; }; RoundCornersModifier.prototype.processPath = function(path, round){ var cloned_path = shape_pool.newElement(); cloned_path.c = path.c; var i, len = path._length; var currentV,currentI,currentO,closerV, newV,newO,newI,distance,newPosPerc,index = 0; var vX,vY,oX,oY,iX,iY; for(i=0;i0){ pos -= 1; //this._elements.unshift(arr.splice(pos,1)[0]); this._elements.unshift(arr[pos]); cont += 1; } if(this.dynamicProperties.length){ this.k = true; }else{ this.getValue(true); } }; RepeaterModifier.prototype.resetElements = function(elements){ var i, len = elements.length; for(i = 0; i < len; i += 1) { elements[i]._processed = false; if(elements[i].ty === 'gr'){ this.resetElements(elements[i].it); } } }; RepeaterModifier.prototype.cloneElements = function(elements){ var i, len = elements.length; var newElements = JSON.parse(JSON.stringify(elements)); this.resetElements(newElements); return newElements; }; RepeaterModifier.prototype.changeGroupRender = function(elements, renderFlag) { var i, len = elements.length; for(i = 0; i < len; i += 1) { elements[i]._render = renderFlag; if(elements[i].ty === 'gr') { this.changeGroupRender(elements[i].it, renderFlag); } } }; RepeaterModifier.prototype.processShapes = function(_isFirstFrame) { var items, itemsTransform, i, dir, cont; if(this._mdf || _isFirstFrame){ var copies = Math.ceil(this.c.v); if(this._groups.length < copies){ while(this._groups.length < copies){ var group = { it:this.cloneElements(this._elements), ty:'gr' }; group.it.push({"a":{"a":0,"ix":1,"k":[0,0]},"nm":"Transform","o":{"a":0,"ix":7,"k":100},"p":{"a":0,"ix":2,"k":[0,0]},"r":{"a":1,"ix":6,"k":[{s:0,e:0,t:0},{s:0,e:0,t:1}]},"s":{"a":0,"ix":3,"k":[100,100]},"sa":{"a":0,"ix":5,"k":0},"sk":{"a":0,"ix":4,"k":0},"ty":"tr"}); this.arr.splice(0,0,group); this._groups.splice(0,0,group); this._currentCopies += 1; } this.elem.reloadShapes(); } cont = 0; var renderFlag; for(i = 0; i <= this._groups.length - 1; i += 1){ renderFlag = cont < copies; this._groups[i]._render = renderFlag; this.changeGroupRender(this._groups[i].it, renderFlag); cont += 1; } this._currentCopies = copies; //// var offset = this.o.v; var offsetModulo = offset%1; var roundOffset = offset > 0 ? Math.floor(offset) : Math.ceil(offset); var k; var tMat = this.tr.v.props; var pProps = this.pMatrix.props; var rProps = this.rMatrix.props; var sProps = this.sMatrix.props; this.pMatrix.reset(); this.rMatrix.reset(); this.sMatrix.reset(); this.tMatrix.reset(); this.matrix.reset(); var iteration = 0; if(offset > 0) { while(iterationroundOffset){ this.applyTransforms(this.pMatrix, this.rMatrix, this.sMatrix, this.tr, 1, true); iteration -= 1; } if(offsetModulo){ this.applyTransforms(this.pMatrix, this.rMatrix, this.sMatrix, this.tr, - offsetModulo, true); iteration -= offsetModulo; } } i = this.data.m === 1 ? 0 : this._currentCopies - 1; dir = this.data.m === 1 ? 1 : -1; cont = this._currentCopies; var j, jLen; while(cont){ items = this.elemsData[i].it; itemsTransform = items[items.length - 1].transform.mProps.v.props; jLen = itemsTransform.length; items[items.length - 1].transform.mProps._mdf = true; items[items.length - 1].transform.op._mdf = true; items[items.length - 1].transform.op.v = this.so.v + (this.eo.v - this.so.v) * (i / (this._currentCopies - 1)); if(iteration !== 0){ if((i !== 0 && dir === 1) || (i !== this._currentCopies - 1 && dir === -1)){ this.applyTransforms(this.pMatrix, this.rMatrix, this.sMatrix, this.tr, 1, false); } this.matrix.transform(rProps[0],rProps[1],rProps[2],rProps[3],rProps[4],rProps[5],rProps[6],rProps[7],rProps[8],rProps[9],rProps[10],rProps[11],rProps[12],rProps[13],rProps[14],rProps[15]); this.matrix.transform(sProps[0],sProps[1],sProps[2],sProps[3],sProps[4],sProps[5],sProps[6],sProps[7],sProps[8],sProps[9],sProps[10],sProps[11],sProps[12],sProps[13],sProps[14],sProps[15]); this.matrix.transform(pProps[0],pProps[1],pProps[2],pProps[3],pProps[4],pProps[5],pProps[6],pProps[7],pProps[8],pProps[9],pProps[10],pProps[11],pProps[12],pProps[13],pProps[14],pProps[15]); for(j=0;j 0.01){ return false; } i += 1; } return true; }; GradientProperty.prototype.checkCollapsable = function() { if (this.o.length/2 !== this.c.length/4) { return false; } if (this.data.k.k[0].s) { var i = 0, len = this.data.k.k.length; while (i < len) { if (!this.comparePoints(this.data.k.k[i].s, this.data.p)) { return false; } i += 1; } } else if(!this.comparePoints(this.data.k.k, this.data.p)) { return false; } return true; }; GradientProperty.prototype.getValue = function(forceRender){ this.prop.getValue(); this._mdf = false; this._cmdf = false; this._omdf = false; if(this.prop._mdf || forceRender){ var i, len = this.data.p*4; var mult, val; for(i=0;i= currentLength + animatorOffset || !points) { perc = (currentLength + animatorOffset - segmentLength) / currentPoint.partialLength; xPathPos = prevPoint.point[0] + (currentPoint.point[0] - prevPoint.point[0]) * perc; yPathPos = prevPoint.point[1] + (currentPoint.point[1] - prevPoint.point[1]) * perc; matrixHelper.translate(-alignment[0]*letters[i].an/200, -(alignment[1] * yOff / 100)); flag = false; } else if (points) { segmentLength += currentPoint.partialLength; pointInd += 1; if (pointInd >= points.length) { pointInd = 0; segmentInd += 1; if (!segments[segmentInd]) { if (mask.v.c) { pointInd = 0; segmentInd = 0; points = segments[segmentInd].points; } else { segmentLength -= currentPoint.partialLength; points = null; } } else { points = segments[segmentInd].points; } } if (points) { prevPoint = currentPoint; currentPoint = points[pointInd]; partialLength = currentPoint.partialLength; } } } offf = letters[i].an / 2 - letters[i].add; matrixHelper.translate(-offf, 0, 0); } else { offf = letters[i].an/2 - letters[i].add; matrixHelper.translate(-offf,0,0); // Grouping alignment matrixHelper.translate(-alignment[0]*letters[i].an/200, -alignment[1]*yOff/100, 0); } lineLength += letters[i].l/2; for(j=0;j 1; if(this.kf) { this.addEffect(this.getKeyframeValue.bind(this)); } return this.kf; } TextProperty.prototype.addEffect = function(effectFunction) { this.effectsSequence.push(effectFunction); this.elem.addDynamicProperty(this); }; TextProperty.prototype.getValue = function(_finalValue) { if((this.elem.globalData.frameId === this.frameId || !this.effectsSequence.length) && !_finalValue) { return; } this.currentData.t = this.data.d.k[this.keysIndex].s.t; var currentValue = this.currentData; var currentIndex = this.keysIndex; if(this.lock) { this.setCurrentData(this.currentData); return; } this.lock = true; this._mdf = false; var multipliedValue; var i, len = this.effectsSequence.length; var finalValue = _finalValue || this.data.d.k[this.keysIndex].s; for(i = 0; i < len; i += 1) { //Checking if index changed to prevent creating a new object every time the expression updates. if(currentIndex !== this.keysIndex) { finalValue = this.effectsSequence[i](finalValue, finalValue.t); } else { finalValue = this.effectsSequence[i](this.currentData, finalValue.t); } } if(currentValue !== finalValue) { this.setCurrentData(finalValue); } this.pv = this.v = this.currentData; this.lock = false; this.frameId = this.elem.globalData.frameId; } TextProperty.prototype.getKeyframeValue = function() { var textKeys = this.data.d.k, textDocumentData; var frameNum = this.elem.comp.renderedFrame; var i = 0, len = textKeys.length; while(i <= len - 1) { textDocumentData = textKeys[i].s; if(i === len - 1 || textKeys[i+1].t > frameNum){ break; } i += 1; } if(this.keysIndex !== i) { this.keysIndex = i; } return this.data.d.k[this.keysIndex].s; }; TextProperty.prototype.buildFinalText = function(text) { var combinedCharacters = FontManager.getCombinedCharacterCodes(); var charactersArray = []; var i = 0, len = text.length; while (i < len) { if (combinedCharacters.indexOf(text.charCodeAt(i)) !== -1) { charactersArray[charactersArray.length - 1] += text.charAt(i); } else { charactersArray.push(text.charAt(i)); } i += 1; } return charactersArray; } TextProperty.prototype.completeTextData = function(documentData) { documentData.__complete = true; var fontManager = this.elem.globalData.fontManager; var data = this.data; var letters = []; var i, len; var newLineFlag, index = 0, val; var anchorGrouping = data.m.g; var currentSize = 0, currentPos = 0, currentLine = 0, lineWidths = []; var lineWidth = 0; var maxLineWidth = 0; var j, jLen; var fontData = fontManager.getFontByName(documentData.f); var charData, cLength = 0; var styles = fontData.fStyle ? fontData.fStyle.split(' ') : []; var fWeight = 'normal', fStyle = 'normal'; len = styles.length; var styleName; for(i=0;i boxWidth && finalText[i] !== ' '){ if(lastSpaceIndex === -1){ len += 1; } else { i = lastSpaceIndex; } currentHeight += documentData.finalLineHeight || documentData.finalSize*1.2; finalText.splice(i, lastSpaceIndex === i ? 1 : 0,"\r"); //finalText = finalText.substr(0,i) + "\r" + finalText.substr(i === lastSpaceIndex ? i + 1 : i); lastSpaceIndex = -1; lineWidth = 0; }else { lineWidth += cLength; lineWidth += trackingOffset; } } currentHeight += fontData.ascent*documentData.finalSize/100; if(this.canResize && documentData.finalSize > this.minimumFontSize && boxHeight < currentHeight) { documentData.finalSize -= 1; documentData.finalLineHeight = documentData.finalSize * documentData.lh / documentData.s; } else { documentData.finalText = finalText; len = documentData.finalText.length; flag = false; } } } lineWidth = - trackingOffset; cLength = 0; var uncollapsedSpaces = 0; var currentChar; for (i = 0;i < len ;i += 1) { newLineFlag = false; currentChar = documentData.finalText[i]; charCode = currentChar.charCodeAt(0); if (currentChar === ' '){ val = '\u00A0'; } else if (charCode === 13 || charCode === 3) { uncollapsedSpaces = 0; lineWidths.push(lineWidth); maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth; lineWidth = - 2 * trackingOffset; val = ''; newLineFlag = true; currentLine += 1; }else{ val = documentData.finalText[i]; } if(fontManager.chars){ charData = fontManager.getCharData(currentChar, fontData.fStyle, fontManager.getFontByName(documentData.f).fFamily); cLength = newLineFlag ? 0 : charData.w*documentData.finalSize/100; }else{ //var charWidth = fontManager.measureText(val, documentData.f, documentData.finalSize); //tCanvasHelper.font = documentData.finalSize + 'px '+ fontManager.getFontByName(documentData.f).fFamily; cLength = fontManager.measureText(val, documentData.f, documentData.finalSize); } // if(currentChar === ' '){ uncollapsedSpaces += cLength + trackingOffset; } else { lineWidth += cLength + trackingOffset + uncollapsedSpaces; uncollapsedSpaces = 0; } letters.push({l:cLength,an:cLength,add:currentSize,n:newLineFlag, anIndexes:[], val: val, line: currentLine, animatorJustifyOffset: 0}); if(anchorGrouping == 2){ currentSize += cLength; if(val === '' || val === '\u00A0' || i === len - 1){ if(val === '' || val === '\u00A0'){ currentSize -= cLength; } while(currentPos<=i){ letters[currentPos].an = currentSize; letters[currentPos].ind = index; letters[currentPos].extra = cLength; currentPos += 1; } index += 1; currentSize = 0; } }else if(anchorGrouping == 3){ currentSize += cLength; if(val === '' || i === len - 1){ if(val === ''){ currentSize -= cLength; } while(currentPos<=i){ letters[currentPos].an = currentSize; letters[currentPos].ind = index; letters[currentPos].extra = cLength; currentPos += 1; } currentSize = 0; index += 1; } }else{ letters[index].ind = index; letters[index].extra = 0; index += 1; } } documentData.l = letters; maxLineWidth = lineWidth > maxLineWidth ? lineWidth : maxLineWidth; lineWidths.push(lineWidth); if(documentData.sz){ documentData.boxWidth = documentData.sz[0]; documentData.justifyOffset = 0; }else{ documentData.boxWidth = maxLineWidth; switch(documentData.j){ case 1: documentData.justifyOffset = - documentData.boxWidth; break; case 2: documentData.justifyOffset = - documentData.boxWidth/2; break; default: documentData.justifyOffset = 0; } } documentData.lineWidths = lineWidths; var animators = data.a, animatorData, letterData; jLen = animators.length; var based, ind, indexes = []; for(j=0;j= e ? 1 : 0; }else{ mult = max(0,min(0.5/(e-s) + (ind-s)/(e-s),1)); } mult = easer(mult); }else if(type == 3){ if(e === s){ mult = ind >= e ? 0 : 1; }else{ mult = 1 - max(0,min(0.5/(e-s) + (ind-s)/(e-s),1)); } mult = easer(mult); }else if(type == 4){ if(e === s){ mult = 0; }else{ mult = max(0,min(0.5/(e-s) + (ind-s)/(e-s),1)); if(mult<0.5){ mult *= 2; }else{ mult = 1 - 2*(mult-0.5); } } mult = easer(mult); }else if(type == 5){ if(e === s){ mult = 0; }else{ var tot = e - s; /*ind += 0.5; mult = -4/(tot*tot)*(ind*ind)+(4/tot)*ind;*/ ind = min(max(0,ind+0.5-s),e-s); var x = -tot/2+ind; var a = tot/2; mult = Math.sqrt(1 - (x*x)/(a*a)); } mult = easer(mult); }else if(type == 6){ if(e === s){ mult = 0; }else{ ind = min(max(0,ind+0.5-s),e-s); mult = (1+(Math.cos((Math.PI+Math.PI*2*(ind)/(e-s)))))/2; /* ind = Math.min(Math.max(s,ind),e-1); mult = (1+(Math.cos((Math.PI+Math.PI*2*(ind-s)/(e-1-s)))))/2; mult = Math.max(mult,(1/(e-1-s))/(e-1-s));*/ } mult = easer(mult); }else { if(ind >= floor(s)){ if(ind-s < 0){ mult = 1 - (s - ind); }else{ mult = max(0,min(e-ind,1)); } } mult = easer(mult); } return mult*this.a.v; }, getValue: function(newCharsFlag) { this.iterateDynamicProperties(); this._mdf = newCharsFlag || this._mdf; this._currentTextLength = this.elem.textProperty.currentData.l.length || 0; if(newCharsFlag && this.data.r === 2) { this.e.v = this._currentTextLength; } var divisor = this.data.r === 2 ? 1 : 100 / this.data.totalChars; var o = this.o.v/divisor; var s = this.s.v/divisor + o; var e = (this.e.v/divisor) + o; if(s>e){ var _s = s; s = e; e = _s; } this.finalS = s; this.finalE = e; } } extendPrototype([DynamicPropertyContainer], TextSelectorProp); function getTextSelectorProp(elem, data,arr) { return new TextSelectorProp(elem, data, arr); } return { getTextSelectorProp: getTextSelectorProp }; }()); var pool_factory = (function() { return function(initialLength, _create, _release, _clone) { var _length = 0; var _maxLength = initialLength; var pool = createSizedArray(_maxLength); var ob = { newElement: newElement, release: release }; function newElement(){ var element; if(_length){ _length -= 1; element = pool[_length]; } else { element = _create(); } return element; } function release(element) { if(_length === _maxLength) { pool = pooling.double(pool); _maxLength = _maxLength*2; } if (_release) { _release(element); } pool[_length] = element; _length += 1; } function clone() { var clonedElement = newElement(); return _clone(clonedElement); } return ob; }; }()); var pooling = (function(){ function double(arr){ return arr.concat(createSizedArray(arr.length)); } return { double: double }; }()); var point_pool = (function(){ function create() { return createTypedArray('float32', 2); } return pool_factory(8, create); }()); var shape_pool = (function(){ function create() { return new ShapePath(); } function release(shapePath) { var len = shapePath._length, i; for(i = 0; i < len; i += 1) { point_pool.release(shapePath.v[i]); point_pool.release(shapePath.i[i]); point_pool.release(shapePath.o[i]); shapePath.v[i] = null; shapePath.i[i] = null; shapePath.o[i] = null; } shapePath._length = 0; shapePath.c = false; } function clone(shape) { var cloned = factory.newElement(); var i, len = shape._length === undefined ? shape.v.length : shape._length; cloned.setLength(len); cloned.c = shape.c; var pt; for(i = 0; i < len; i += 1) { cloned.setTripleAt(shape.v[i][0],shape.v[i][1],shape.o[i][0],shape.o[i][1],shape.i[i][0],shape.i[i][1], i); } return cloned; } var factory = pool_factory(4, create, release); factory.clone = clone; return factory; }()); var shapeCollection_pool = (function(){ var ob = { newShapeCollection: newShapeCollection, release: release }; var _length = 0; var _maxLength = 4; var pool = createSizedArray(_maxLength); function newShapeCollection(){ var shapeCollection; if(_length){ _length -= 1; shapeCollection = pool[_length]; } else { shapeCollection = new ShapeCollection(); } return shapeCollection; } function release(shapeCollection) { var i, len = shapeCollection._length; for(i = 0; i < len; i += 1) { shape_pool.release(shapeCollection.shapes[i]); } shapeCollection._length = 0; if(_length === _maxLength) { pool = pooling.double(pool); _maxLength = _maxLength*2; } pool[_length] = shapeCollection; _length += 1; } return ob; }()); var segments_length_pool = (function(){ function create() { return { lengths: [], totalLength: 0 }; } function release(element) { var i, len = element.lengths.length; for(i=0;i= 0; i--) { if (!this.elements[i]) { data = this.layers[i]; if(data.ip - data.st <= (num - this.layers[i].st) && data.op - data.st > (num - this.layers[i].st)) { this.buildItem(i); } } this.completeLayers = this.elements[i] ? this.completeLayers:false; } this.checkPendingElements(); }; BaseRenderer.prototype.createItem = function(layer){ switch(layer.ty){ case 2: return this.createImage(layer); case 0: return this.createComp(layer); case 1: return this.createSolid(layer); case 3: return this.createNull(layer); case 4: return this.createShape(layer); case 5: return this.createText(layer); case 13: return this.createCamera(layer); } return this.createNull(layer); }; BaseRenderer.prototype.createCamera = function(){ throw new Error('You\'re using a 3d camera. Try the html renderer.'); }; BaseRenderer.prototype.buildAllItems = function(){ var i, len = this.layers.length; for(i=0;i= 0; i--) { if(this.completeLayers || this.elements[i]){ this.elements[i].prepareFrame(num - this.layers[i].st); } } if(this.globalData._mdf) { for (i = 0; i < len; i += 1) { if(this.completeLayers || this.elements[i]){ this.elements[i].renderFrame(); } } } }; SVGRenderer.prototype.appendElementInPos = function(element, pos){ var newElement = element.getBaseElement(); if(!newElement){ return; } var i = 0; var nextElement; while(ielementRel && fillType === 'meet' || animationRelelementRel && fillType === 'slice'))){ this.transformCanvas.tx = (elementWidth-this.transformCanvas.w*(elementHeight/this.transformCanvas.h))/2*this.renderConfig.dpr; } else if(xPos === 'xMax' && ((animationRelelementRel && fillType === 'slice'))){ this.transformCanvas.tx = (elementWidth-this.transformCanvas.w*(elementHeight/this.transformCanvas.h))*this.renderConfig.dpr; } else { this.transformCanvas.tx = 0; } if(yPos === 'YMid' && ((animationRel>elementRel && fillType==='meet') || (animationRelelementRel && fillType==='meet') || (animationRel= 0; i-=1) { if(this.elements[i]) { this.elements[i].destroy(); } } this.elements.length = 0; this.globalData.canvasContext = null; this.animationItem.container = null; this.destroyed = true; }; CanvasRenderer.prototype.renderFrame = function(num, forceRender){ if((this.renderedFrame === num && this.renderConfig.clearCanvas === true && !forceRender) || this.destroyed || num === -1){ return; } this.renderedFrame = num; this.globalData.frameNum = num - this.animationItem._isFirstFrame; this.globalData.frameId += 1; this.globalData._mdf = !this.renderConfig.clearCanvas || forceRender; this.globalData.projectInterface.currentFrame = num; // console.log('--------'); // console.log('NEW: ',num); var i, len = this.layers.length; if(!this.completeLayers){ this.checkLayers(num); } for (i = 0; i < len; i++) { if(this.completeLayers || this.elements[i]){ this.elements[i].prepareFrame(num - this.layers[i].st); } } if(this.globalData._mdf) { if(this.renderConfig.clearCanvas === true){ this.canvasContext.clearRect(0, 0, this.transformCanvas.w, this.transformCanvas.h); }else{ this.save(); } for (i = len - 1; i >= 0; i-=1) { if(this.completeLayers || this.elements[i]){ this.elements[i].renderFrame(); } } if(this.renderConfig.clearCanvas !== true){ this.restore(); } } }; CanvasRenderer.prototype.buildItem = function(pos){ var elements = this.elements; if(elements[pos] || this.layers[pos].ty == 99){ return; } var element = this.createItem(this.layers[pos], this,this.globalData); elements[pos] = element; element.initExpressions(); /*if(this.layers[pos].ty === 0){ element.resize(this.globalData.transformCanvas); }*/ }; CanvasRenderer.prototype.checkPendingElements = function(){ while(this.pendingElements.length){ var element = this.pendingElements.pop(); element.checkParenting(); } }; CanvasRenderer.prototype.hide = function(){ this.animationItem.container.style.display = 'none'; }; CanvasRenderer.prototype.show = function(){ this.animationItem.container.style.display = 'block'; }; CanvasRenderer.prototype.configAnimation = function(animData){ if(this.animationItem.wrapper){ this.animationItem.container = createTag('canvas'); this.animationItem.container.style.width = '100%'; this.animationItem.container.style.height = '100%'; //this.animationItem.container.style.transform = 'translate3d(0,0,0)'; //this.animationItem.container.style.webkitTransform = 'translate3d(0,0,0)'; this.animationItem.container.style.transformOrigin = this.animationItem.container.style.mozTransformOrigin = this.animationItem.container.style.webkitTransformOrigin = this.animationItem.container.style['-webkit-transform'] = "0px 0px 0px"; this.animationItem.wrapper.appendChild(this.animationItem.container); this.canvasContext = this.animationItem.container.getContext('2d'); if(this.renderConfig.className) { this.animationItem.container.setAttribute('class', this.renderConfig.className); } }else{ this.canvasContext = this.renderConfig.context; } this.data = animData; this.layers = animData.layers; this.transformCanvas = { w: animData.w, h:animData.h, sx:0, sy:0, tx:0, ty:0 }; this.globalData.frameId = 0; this.globalData.frameRate = animData.fr; this.globalData.nm = animData.nm; this.globalData.compSize = { w: animData.w, h: animData.h } this.globalData.canvasContext = this.canvasContext; this.globalData.renderer = this; this.globalData.isDashed = false; this.globalData.progressiveLoad = this.renderConfig.progressiveLoad; this.globalData.transformCanvas = this.transformCanvas; this.elements = createSizedArray(animData.layers.length); this.updateContainerSize(); }; function MaskElement(data,element,globalData) { this.data = data; this.element = element; this.globalData = globalData; this.storedData = []; this.masksProperties = this.data.masksProperties || []; this.maskElement = null; var defs = this.globalData.defs; var i, len = this.masksProperties ? this.masksProperties.length : 0; this.viewData = createSizedArray(len); this.solidPath = ''; var path, properties = this.masksProperties; var count = 0; var currentMasks = []; var j, jLen; var layerId = createElementID(); var rect, expansor, feMorph,x; var maskType = 'clipPath', maskRef = 'clip-path'; for (i = 0; i < len; i++) { if((properties[i].mode !== 'a' && properties[i].mode !== 'n')|| properties[i].inv || properties[i].o.k !== 100){ maskType = 'mask'; maskRef = 'mask'; } if((properties[i].mode == 's' || properties[i].mode == 'i') && count === 0){ rect = createNS( 'rect'); rect.setAttribute('fill', '#ffffff'); rect.setAttribute('width', this.element.comp.data.w || 0); rect.setAttribute('height', this.element.comp.data.h || 0); currentMasks.push(rect); } else { rect = null; } path = createNS( 'path'); if(properties[i].mode == 'n') { // TODO move this to a factory or to a constructor this.viewData[i] = { op: PropertyFactory.getProp(this.element,properties[i].o,0,0.01,this.element), prop: ShapePropertyFactory.getShapeProp(this.element,properties[i],3), elem: path, lastPath: '' }; defs.appendChild(path); continue; } count += 1; path.setAttribute('fill', properties[i].mode === 's' ? '#000000':'#ffffff'); path.setAttribute('clip-rule','nonzero'); var filterID; if (properties[i].x.k !== 0) { maskType = 'mask'; maskRef = 'mask'; x = PropertyFactory.getProp(this.element,properties[i].x,0,null,this.element); filterID = createElementID(); expansor = createNS('filter'); expansor.setAttribute('id',filterID); feMorph = createNS('feMorphology'); feMorph.setAttribute('operator','erode'); feMorph.setAttribute('in','SourceGraphic'); feMorph.setAttribute('radius','0'); expansor.appendChild(feMorph); defs.appendChild(expansor); path.setAttribute('stroke', properties[i].mode === 's' ? '#000000':'#ffffff'); } else { feMorph = null; x = null; } // TODO move this to a factory or to a constructor this.storedData[i] = { elem: path, x: x, expan: feMorph, lastPath: '', lastOperator:'', filterId:filterID, lastRadius:0 }; if(properties[i].mode == 'i'){ jLen = currentMasks.length; var g = createNS('g'); for(j=0;j 0){ this.maskElement.setAttribute('id', layerId); this.element.maskedElement.setAttribute(maskRef, "url(" + locationHref + "#" + layerId + ")"); defs.appendChild(this.maskElement); } if (this.viewData.length) { this.element.addRenderableComponent(this); } } MaskElement.prototype.getMaskProperty = function(pos){ return this.viewData[pos].prop; }; MaskElement.prototype.renderFrame = function (isFirstFrame) { var finalMat = this.element.finalTransform.mat; var i, len = this.masksProperties.length; for (i = 0; i < len; i++) { if(this.viewData[i].prop._mdf || isFirstFrame){ this.drawPath(this.masksProperties[i],this.viewData[i].prop.v,this.viewData[i]); } if(this.viewData[i].op._mdf || isFirstFrame){ this.viewData[i].elem.setAttribute('fill-opacity',this.viewData[i].op.v); } if(this.masksProperties[i].mode !== 'n'){ if(this.viewData[i].invRect && (this.element.finalTransform.mProp._mdf || isFirstFrame)){ this.viewData[i].invRect.setAttribute('x', -finalMat.props[12]); this.viewData[i].invRect.setAttribute('y', -finalMat.props[13]); } if(this.storedData[i].x && (this.storedData[i].x._mdf || isFirstFrame)){ var feMorph = this.storedData[i].expan; if(this.storedData[i].x.v < 0){ if(this.storedData[i].lastOperator !== 'erode'){ this.storedData[i].lastOperator = 'erode'; this.storedData[i].elem.setAttribute('filter','url(' + locationHref + '#'+this.storedData[i].filterId+')'); } feMorph.setAttribute('radius',-this.storedData[i].x.v); }else{ if(this.storedData[i].lastOperator !== 'dilate'){ this.storedData[i].lastOperator = 'dilate'; this.storedData[i].elem.setAttribute('filter',null); } this.storedData[i].elem.setAttribute('stroke-width', this.storedData[i].x.v*2); } } } } }; MaskElement.prototype.getMaskelement = function () { return this.maskElement; }; MaskElement.prototype.createLayerSolidPath = function(){ var path = 'M0,0 '; path += ' h' + this.globalData.compSize.w ; path += ' v' + this.globalData.compSize.h ; path += ' h-' + this.globalData.compSize.w ; path += ' v-' + this.globalData.compSize.h + ' '; return path; }; MaskElement.prototype.drawPath = function(pathData,pathNodes,viewData){ var pathString = " M"+pathNodes.v[0][0]+','+pathNodes.v[0][1]; var i, len; len = pathNodes._length; for(i=1;i 1){ pathString += " C"+pathNodes.o[i-1][0]+','+pathNodes.o[i-1][1] + " "+pathNodes.i[0][0]+','+pathNodes.i[0][1] + " "+pathNodes.v[0][0]+','+pathNodes.v[0][1]; } //pathNodes.__renderedString = pathString; if(viewData.lastPath !== pathString){ var pathShapeValue = ''; if(viewData.elem){ if(pathNodes.c){ pathShapeValue = pathData.inv ? this.solidPath + pathString : pathString; } viewData.elem.setAttribute('d',pathShapeValue); } viewData.lastPath = pathString; } }; MaskElement.prototype.destroy = function(){ this.element = null; this.globalData = null; this.maskElement = null; this.data = null; this.masksProperties = null; }; /** * @file * Handles AE's layer parenting property. * */ function HierarchyElement(){} HierarchyElement.prototype = { /** * @function * Initializes hierarchy properties * */ initHierarchy: function() { //element's parent list this.hierarchy = []; //if element is parent of another layer _isParent will be true this._isParent = false; this.checkParenting(); }, /** * @function * Sets layer's hierarchy. * @param {array} hierarch * layer's parent list * */ setHierarchy: function(hierarchy){ this.hierarchy = hierarchy; }, /** * @function * Sets layer as parent. * */ setAsParent: function() { this._isParent = true; }, /** * @function * Searches layer's parenting chain * */ checkParenting: function(){ if (this.data.parent !== undefined){ this.comp.buildElementParenting(this, this.data.parent, []); } } }; /** * @file * Handles element's layer frame update. * Checks layer in point and out point * */ function FrameElement(){} FrameElement.prototype = { /** * @function * Initializes frame related properties. * */ initFrame: function(){ //set to true when inpoint is rendered this._isFirstFrame = false; //list of animated properties this.dynamicProperties = []; // If layer has been modified in current tick this will be true this._mdf = false; }, /** * @function * Calculates all dynamic values * * @param {number} num * current frame number in Layer's time * @param {boolean} isVisible * if layers is currently in range * */ prepareProperties: function(num, isVisible) { var i, len = this.dynamicProperties.length; for (i = 0;i < len; i += 1) { if (isVisible || (this._isParent && this.dynamicProperties[i].propType === 'transform')) { this.dynamicProperties[i].getValue(); if (this.dynamicProperties[i]._mdf) { this.globalData._mdf = true; this._mdf = true; } } } }, addDynamicProperty: function(prop) { if(this.dynamicProperties.indexOf(prop) === -1) { this.dynamicProperties.push(prop); } } }; function TransformElement(){} TransformElement.prototype = { initTransform: function() { this.finalTransform = { mProp: this.data.ks ? TransformPropertyFactory.getTransformProperty(this, this.data.ks, this) : {o:0}, _matMdf: false, _opMdf: false, mat: new Matrix() }; if (this.data.ao) { this.finalTransform.mProp.autoOriented = true; } //TODO: check TYPE 11: Guided elements if (this.data.ty !== 11) { //this.createElements(); } }, renderTransform: function() { this.finalTransform._opMdf = this.finalTransform.mProp.o._mdf || this._isFirstFrame; this.finalTransform._matMdf = this.finalTransform.mProp._mdf || this._isFirstFrame; if (this.hierarchy) { var mat; var finalMat = this.finalTransform.mat; var i = 0, len = this.hierarchy.length; //Checking if any of the transformation matrices in the hierarchy chain has changed. if (!this.finalTransform._matMdf) { while (i < len) { if (this.hierarchy[i].finalTransform.mProp._mdf) { this.finalTransform._matMdf = true; break; } i += 1; } } if (this.finalTransform._matMdf) { mat = this.finalTransform.mProp.v.props; finalMat.cloneFromProps(mat); for (i = 0; i < len; i += 1) { mat = this.hierarchy[i].finalTransform.mProp.v.props; finalMat.transform(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5], mat[6], mat[7], mat[8], mat[9], mat[10], mat[11], mat[12], mat[13], mat[14], mat[15]); } } } }, globalToLocal: function(pt) { var transforms = []; transforms.push(this.finalTransform); var flag = true; var comp = this.comp; while (flag) { if (comp.finalTransform) { if (comp.data.hasMask) { transforms.splice(0, 0, comp.finalTransform); } comp = comp.comp; } else { flag = false; } } var i, len = transforms.length,ptNew; for (i = 0; i < len; i += 1) { ptNew = transforms[i].mat.applyToPointArray(0, 0, 0); //ptNew = transforms[i].mat.applyToPointArray(pt[0],pt[1],pt[2]); pt = [pt[0] - ptNew[0], pt[1] - ptNew[1], 0]; } return pt; }, mHelper: new Matrix() }; function RenderableElement(){ } RenderableElement.prototype = { initRenderable: function() { //layer's visibility related to inpoint and outpoint. Rename isVisible to isInRange this.isInRange = false; //layer's display state this.hidden = false; // If layer's transparency equals 0, it can be hidden this.isTransparent = false; //list of animated components this.renderableComponents = []; }, addRenderableComponent: function(component) { if(this.renderableComponents.indexOf(component) === -1) { this.renderableComponents.push(component); } }, removeRenderableComponent: function(component) { if(this.renderableComponents.indexOf(component) !== -1) { this.renderableComponents.splice(this.renderableComponents.indexOf(component), 1); } }, prepareRenderableFrame: function(num) { this.checkLayerLimits(num); }, checkTransparency: function(){ if(this.finalTransform.mProp.o.v <= 0) { if(!this.isTransparent && this.globalData.renderConfig.hideOnTransparent){ this.isTransparent = true; this.hide(); } } else if(this.isTransparent) { this.isTransparent = false; this.show(); } }, /** * @function * Initializes frame related properties. * * @param {number} num * current frame number in Layer's time * */ checkLayerLimits: function(num) { if(this.data.ip - this.data.st <= num && this.data.op - this.data.st > num) { if(this.isInRange !== true){ this.globalData._mdf = true; this._mdf = true; this.isInRange = true; this.show(); } } else { if(this.isInRange !== false){ this.globalData._mdf = true; this.isInRange = false; this.hide(); } } }, renderRenderable: function() { var i, len = this.renderableComponents.length; for(i = 0; i < len; i += 1) { this.renderableComponents[i].renderFrame(this._isFirstFrame); } /*this.maskManager.renderFrame(this.finalTransform.mat); this.renderableEffectsManager.renderFrame(this._isFirstFrame);*/ }, sourceRectAtTime: function(){ return { top:0, left:0, width:100, height:100 }; }, getLayerSize: function(){ if(this.data.ty === 5){ return {w:this.data.textData.width,h:this.data.textData.height}; }else{ return {w:this.data.width,h:this.data.height}; } } }; function RenderableDOMElement() {} (function(){ var _prototype = { initElement: function(data,globalData,comp) { this.initFrame(); this.initBaseData(data, globalData, comp); this.initTransform(data, globalData, comp); this.initHierarchy(); this.initRenderable(); this.initRendererElement(); this.createContainerElements(); this.createRenderableComponents(); this.createContent(); this.hide(); }, hide: function(){ if (!this.hidden && (!this.isInRange || this.isTransparent)) { var elem = this.baseElement || this.layerElement; elem.style.display = 'none'; this.hidden = true; } }, show: function(){ if (this.isInRange && !this.isTransparent){ if (!this.data.hd) { var elem = this.baseElement || this.layerElement; elem.style.display = 'block'; } this.hidden = false; this._isFirstFrame = true; } }, renderFrame: function() { //If it is exported as hidden (data.hd === true) no need to render //If it is not visible no need to render if (this.data.hd || this.hidden) { return; } this.renderTransform(); this.renderRenderable(); this.renderElement(); this.renderInnerContent(); if (this._isFirstFrame) { this._isFirstFrame = false; } }, renderInnerContent: function() {}, prepareFrame: function(num) { this._mdf = false; this.prepareRenderableFrame(num); this.prepareProperties(num, this.isInRange); this.checkTransparency(); }, destroy: function(){ this.innerElem = null; this.destroyBaseElement(); } }; extendPrototype([RenderableElement, createProxyFunction(_prototype)], RenderableDOMElement); }()); function ProcessedElement(element, position) { this.elem = element; this.pos = position; } function SVGShapeData(transformers, level, shape) { this.caches = []; this.styles = []; this.transformers = transformers; this.lStr = ''; this.sh = shape; this.lvl = level; //TODO find if there are some cases where _isAnimated can be false. // For now, since shapes add up with other shapes. They have to be calculated every time. // One way of finding out is checking if all styles associated to this shape depend only of this shape this._isAnimated = !!shape.k; // TODO: commenting this for now since all shapes are animated var i = 0, len = transformers.length; while(i < len) { if(transformers[i].mProps.dynamicProperties.length) { this._isAnimated = true; break; } i += 1; } } SVGShapeData.prototype.setAsAnimated = function() { this._isAnimated = true; } function ShapeGroupData() { this.it = []; this.prevViewData = []; this.gr = createNS('g'); } function ShapeTransformManager() { this.sequences = {}; this.sequenceList = []; this.transform_key_count = 0; } ShapeTransformManager.prototype = { addTransformSequence: function(transforms) { var i, len = transforms.length; var key = '_'; for(i = 0; i < len; i += 1) { key += transforms[i].transform.key + '_'; } var sequence = this.sequences[key]; if(!sequence) { sequence = { transforms: [].concat(transforms), finalTransform: new Matrix(), _mdf: false }; this.sequences[key] = sequence; this.sequenceList.push(sequence); } return sequence; }, processSequence: function(sequence, isFirstFrame) { var i = 0, len = sequence.transforms.length, _mdf = isFirstFrame; while (i < len && !isFirstFrame) { if (sequence.transforms[i].transform.mProps._mdf) { _mdf = true; break; } i += 1 } if (_mdf) { var props; sequence.finalTransform.reset(); for (i = len - 1; i >= 0; i -= 1) { props = sequence.transforms[i].transform.mProps.v.props; sequence.finalTransform.transform(props[0],props[1],props[2],props[3],props[4],props[5],props[6],props[7],props[8],props[9],props[10],props[11],props[12],props[13],props[14],props[15]); } } sequence._mdf = _mdf; }, processSequences: function(isFirstFrame) { var i, len = this.sequenceList.length; for (i = 0; i < len; i += 1) { this.processSequence(this.sequenceList[i], isFirstFrame); } }, getNewKey: function() { return '_' + this.transform_key_count++; } } function CVShapeData(element, data, styles, transformsManager) { this.styledShapes = []; this.tr = [0,0,0,0,0,0]; var ty = 4; if(data.ty == 'rc'){ ty = 5; }else if(data.ty == 'el'){ ty = 6; }else if(data.ty == 'sr'){ ty = 7; } this.sh = ShapePropertyFactory.getShapeProp(element,data,ty,element); var i , len = styles.length,styledShape; for (i = 0; i < len; i += 1) { if (!styles[i].closed) { styledShape = { transforms: transformsManager.addTransformSequence(styles[i].transforms), trNodes: [] } this.styledShapes.push(styledShape); styles[i].elements.push(styledShape); } } } CVShapeData.prototype.setAsAnimated = SVGShapeData.prototype.setAsAnimated; function BaseElement(){ } BaseElement.prototype = { checkMasks: function(){ if(!this.data.hasMask){ return false; } var i = 0, len = this.data.masksProperties.length; while(i=0;i-=1){ this.shapeModifiers[i].processShapes(this._isFirstFrame); } }, lcEnum: { '1': 'butt', '2': 'round', '3': 'square' }, ljEnum: { '1': 'miter', '2': 'round', '3': 'bevel' }, searchProcessedElement: function(elem){ var elements = this.processedElements; var i = 0, len = elements.length; while (i < len) { if (elements[i].elem === elem) { return elements[i].pos; } i += 1; } return 0; }, addProcessedElement: function(elem, pos){ var elements = this.processedElements; var i = elements.length; while(i) { i -= 1; if (elements[i].elem === elem) { elements[i].pos = pos; return; } } elements.push(new ProcessedElement(elem, pos)); }, prepareFrame: function(num) { this.prepareRenderableFrame(num); this.prepareProperties(num, this.isInRange); } }; function ITextElement(){ } ITextElement.prototype.initElement = function(data,globalData,comp){ this.lettersChangedFlag = true; this.initFrame(); this.initBaseData(data, globalData, comp); this.textProperty = new TextProperty(this, data.t, this.dynamicProperties); this.textAnimator = new TextAnimatorProperty(data.t, this.renderType, this); this.initTransform(data, globalData, comp); this.initHierarchy(); this.initRenderable(); this.initRendererElement(); this.createContainerElements(); this.createRenderableComponents(); this.createContent(); this.hide(); this.textAnimator.searchProperties(this.dynamicProperties); }; ITextElement.prototype.prepareFrame = function(num) { this._mdf = false; this.prepareRenderableFrame(num); this.prepareProperties(num, this.isInRange); if(this.textProperty._mdf || this.textProperty._isFirstFrame) { this.buildNewText(); this.textProperty._isFirstFrame = false; this.textProperty._mdf = false; } }; ITextElement.prototype.createPathShape = function(matrixHelper, shapes) { var j,jLen = shapes.length; var k, kLen, pathNodes; var shapeStr = ''; for(j=0;j= 0; i -= 1 ){ if(this.completeLayers || this.elements[i]){ this.elements[i].prepareFrame(this.renderedFrame - this.layers[i].st); if(this.elements[i]._mdf) { this._mdf = true; } } } }; ICompElement.prototype.renderInnerContent = function() { var i,len = this.layers.length; for( i = 0; i < len; i += 1 ){ if(this.completeLayers || this.elements[i]){ this.elements[i].renderFrame(); } } }; ICompElement.prototype.setElements = function(elems){ this.elements = elems; }; ICompElement.prototype.getElements = function(){ return this.elements; }; ICompElement.prototype.destroyElements = function(){ var i,len = this.layers.length; for( i = 0; i < len; i+=1 ){ if(this.elements[i]){ this.elements[i].destroy(); } } }; ICompElement.prototype.destroy = function(){ this.destroyElements(); this.destroyBaseElement(); }; function IImageElement(data,globalData,comp){ this.assetData = globalData.getAssetData(data.refId); this.initElement(data,globalData,comp); this.sourceRect = {top:0,left:0,width:this.assetData.w,height:this.assetData.h}; } extendPrototype([BaseElement,TransformElement,SVGBaseElement,HierarchyElement,FrameElement,RenderableDOMElement], IImageElement); IImageElement.prototype.createContent = function(){ var assetPath = this.globalData.getAssetsPath(this.assetData); this.innerElem = createNS('image'); this.innerElem.setAttribute('width',this.assetData.w+"px"); this.innerElem.setAttribute('height',this.assetData.h+"px"); this.innerElem.setAttribute('preserveAspectRatio',this.assetData.pr || this.globalData.renderConfig.imagePreserveAspectRatio); this.innerElem.setAttributeNS('http://www.w3.org/1999/xlink','href',assetPath); this.layerElement.appendChild(this.innerElem); }; IImageElement.prototype.sourceRectAtTime = function() { return this.sourceRect; } function ISolidElement(data,globalData,comp){ this.initElement(data,globalData,comp); } extendPrototype([IImageElement], ISolidElement); ISolidElement.prototype.createContent = function(){ var rect = createNS('rect'); ////rect.style.width = this.data.sw; ////rect.style.height = this.data.sh; ////rect.style.fill = this.data.sc; rect.setAttribute('width',this.data.sw); rect.setAttribute('height',this.data.sh); rect.setAttribute('fill',this.data.sc); this.layerElement.appendChild(rect); }; function SVGShapeElement(data,globalData,comp){ //List of drawable elements this.shapes = []; // Full shape data this.shapesData = data.shapes; //List of styles that will be applied to shapes this.stylesList = []; //List of modifiers that will be applied to shapes this.shapeModifiers = []; //List of items in shape tree this.itemsData = []; //List of items in previous shape tree this.processedElements = []; // List of animated components this.animatedContents = []; this.initElement(data,globalData,comp); //Moving any property that doesn't get too much access after initialization because of v8 way of handling more than 10 properties. // List of elements that have been created this.prevViewData = []; //Moving any property that doesn't get too much access after initialization because of v8 way of handling more than 10 properties. } extendPrototype([BaseElement,TransformElement,SVGBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableDOMElement], SVGShapeElement); SVGShapeElement.prototype.initSecondaryElement = function() { }; SVGShapeElement.prototype.identityMatrix = new Matrix(); SVGShapeElement.prototype.buildExpressionInterface = function(){}; SVGShapeElement.prototype.createContent = function(){ this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement, 0, [], true); this.filterUniqueShapes(); }; /* This method searches for multiple shapes that affect a single element and one of them is animated */ SVGShapeElement.prototype.filterUniqueShapes = function(){ var i, len = this.shapes.length, shape; var j, jLen = this.stylesList.length; var style, count = 0; var tempShapes = []; var areAnimated = false; for(j = 0; j < jLen; j += 1) { style = this.stylesList[j]; areAnimated = false; tempShapes.length = 0; for(i = 0; i < len; i += 1) { shape = this.shapes[i]; if(shape.styles.indexOf(style) !== -1) { tempShapes.push(shape); areAnimated = shape._isAnimated || areAnimated; } } if(tempShapes.length > 1 && areAnimated) { this.setShapesAsAnimated(tempShapes); } } } SVGShapeElement.prototype.setShapesAsAnimated = function(shapes){ var i, len = shapes.length; for(i = 0; i < len; i += 1) { shapes[i].setAsAnimated(); } } SVGShapeElement.prototype.createStyleElement = function(data, level){ //TODO: prevent drawing of hidden styles var elementData; var styleOb = new SVGStyleData(data, level); var pathElement = styleOb.pElem; if(data.ty === 'st') { elementData = new SVGStrokeStyleData(this, data, styleOb); } else if(data.ty === 'fl') { elementData = new SVGFillStyleData(this, data, styleOb); } else if(data.ty === 'gf' || data.ty === 'gs') { var gradientConstructor = data.ty === 'gf' ? SVGGradientFillStyleData : SVGGradientStrokeStyleData; elementData = new gradientConstructor(this, data, styleOb); this.globalData.defs.appendChild(elementData.gf); if (elementData.maskId) { this.globalData.defs.appendChild(elementData.ms); this.globalData.defs.appendChild(elementData.of); pathElement.setAttribute('mask','url(' + locationHref + '#' + elementData.maskId + ')'); } } if(data.ty === 'st' || data.ty === 'gs') { pathElement.setAttribute('stroke-linecap', this.lcEnum[data.lc] || 'round'); pathElement.setAttribute('stroke-linejoin',this.ljEnum[data.lj] || 'round'); pathElement.setAttribute('fill-opacity','0'); if(data.lj === 1) { pathElement.setAttribute('stroke-miterlimit',data.ml); } } if(data.r === 2) { pathElement.setAttribute('fill-rule', 'evenodd'); } if(data.ln){ pathElement.setAttribute('id',data.ln); } if(data.cl){ pathElement.setAttribute('class',data.cl); } if(data.bm){ pathElement.style['mix-blend-mode'] = getBlendMode(data.bm); } this.stylesList.push(styleOb); this.addToAnimatedContents(data, elementData); return elementData; }; SVGShapeElement.prototype.createGroupElement = function(data) { var elementData = new ShapeGroupData(); if(data.ln){ elementData.gr.setAttribute('id',data.ln); } if(data.cl){ elementData.gr.setAttribute('class',data.cl); } if(data.bm){ elementData.gr.style['mix-blend-mode'] = getBlendMode(data.bm); } return elementData; }; SVGShapeElement.prototype.createTransformElement = function(data, container) { var transformProperty = TransformPropertyFactory.getTransformProperty(this,data,this); var elementData = new SVGTransformData(transformProperty, transformProperty.o, container); this.addToAnimatedContents(data, elementData); return elementData; }; SVGShapeElement.prototype.createShapeElement = function(data, ownTransformers, level) { var ty = 4; if(data.ty === 'rc'){ ty = 5; }else if(data.ty === 'el'){ ty = 6; }else if(data.ty === 'sr'){ ty = 7; } var shapeProperty = ShapePropertyFactory.getShapeProp(this,data,ty,this); var elementData = new SVGShapeData(ownTransformers, level, shapeProperty); this.shapes.push(elementData); this.addShapeToModifiers(elementData); this.addToAnimatedContents(data, elementData); return elementData; }; SVGShapeElement.prototype.addToAnimatedContents = function(data, element) { var i = 0, len = this.animatedContents.length; while(i < len) { if(this.animatedContents[i].element === element) { return; } i += 1; } this.animatedContents.push({ fn: SVGElementsRenderer.createRenderFunction(data), element: element, data: data }); }; SVGShapeElement.prototype.setElementStyles = function(elementData){ var arr = elementData.styles; var j, jLen = this.stylesList.length; for (j = 0; j < jLen; j += 1) { if (!this.stylesList[j].closed) { arr.push(this.stylesList[j]); } } }; SVGShapeElement.prototype.reloadShapes = function(){ this._isFirstFrame = true; var i, len = this.itemsData.length; for( i = 0; i < len; i += 1) { this.prevViewData[i] = this.itemsData[i]; } this.searchShapes(this.shapesData,this.itemsData,this.prevViewData,this.layerElement, 0, [], true); this.filterUniqueShapes(); len = this.dynamicProperties.length; for(i = 0; i < len; i += 1) { this.dynamicProperties[i].getValue(); } this.renderModifiers(); }; SVGShapeElement.prototype.searchShapes = function(arr,itemsData,prevViewData,container, level, transformers, render){ var ownTransformers = [].concat(transformers); var i, len = arr.length - 1; var j, jLen; var ownStyles = [], ownModifiers = [], styleOb, currentTransform, modifier, processedPos; for(i=len;i>=0;i-=1){ processedPos = this.searchProcessedElement(arr[i]); if(!processedPos){ arr[i]._render = render; } else { itemsData[i] = prevViewData[processedPos - 1]; } if(arr[i].ty == 'fl' || arr[i].ty == 'st' || arr[i].ty == 'gf' || arr[i].ty == 'gs'){ if(!processedPos){ itemsData[i] = this.createStyleElement(arr[i], level); } else { itemsData[i].style.closed = false; } if(arr[i]._render){ container.appendChild(itemsData[i].style.pElem); } ownStyles.push(itemsData[i].style); }else if(arr[i].ty == 'gr'){ if(!processedPos){ itemsData[i] = this.createGroupElement(arr[i]); } else { jLen = itemsData[i].it.length; for(j=0;j= 0; i -= 1 ){ if(this.completeLayers || this.elements[i]){ this.elements[i].renderFrame(); } } }; CVCompElement.prototype.destroy = function(){ var i,len = this.layers.length; for( i = len - 1; i >= 0; i -= 1 ){ if(this.elements[i]) { this.elements[i].destroy(); } } this.layers = null; this.elements = null; }; function CVMaskElement(data,element){ this.data = data; this.element = element; this.masksProperties = this.data.masksProperties || []; this.viewData = createSizedArray(this.masksProperties.length); var i, len = this.masksProperties.length, hasMasks = false; for (i = 0; i < len; i++) { if(this.masksProperties[i].mode !== 'n'){ hasMasks = true; } this.viewData[i] = ShapePropertyFactory.getShapeProp(this.element,this.masksProperties[i],3); } this.hasMasks = hasMasks; if(hasMasks) { this.element.addRenderableComponent(this); } } CVMaskElement.prototype.renderFrame = function () { if(!this.hasMasks){ return; } var transform = this.element.finalTransform.mat; var ctx = this.element.canvasContext; var i, len = this.masksProperties.length; var pt,pts,data; ctx.beginPath(); for (i = 0; i < len; i++) { if(this.masksProperties[i].mode !== 'n'){ if (this.masksProperties[i].inv) { ctx.moveTo(0, 0); ctx.lineTo(this.element.globalData.compSize.w, 0); ctx.lineTo(this.element.globalData.compSize.w, this.element.globalData.compSize.h); ctx.lineTo(0, this.element.globalData.compSize.h); ctx.lineTo(0, 0); } data = this.viewData[i].v; pt = transform.applyToPointArray(data.v[0][0],data.v[0][1],0); ctx.moveTo(pt[0], pt[1]); var j, jLen = data._length; for (j = 1; j < jLen; j++) { pts = transform.applyToTriplePoints(data.o[j - 1], data.i[j], data.v[j]); ctx.bezierCurveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); } pts = transform.applyToTriplePoints(data.o[j - 1], data.i[0], data.v[0]); ctx.bezierCurveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); } } this.element.globalData.renderer.save(true); ctx.clip(); }; CVMaskElement.prototype.getMaskProperty = MaskElement.prototype.getMaskProperty; CVMaskElement.prototype.destroy = function(){ this.element = null; }; function CVShapeElement(data, globalData, comp) { this.shapes = []; this.shapesData = data.shapes; this.stylesList = []; this.itemsData = []; this.prevViewData = []; this.shapeModifiers = []; this.processedElements = []; this.transformsManager = new ShapeTransformManager(); this.initElement(data, globalData, comp); } extendPrototype([BaseElement,TransformElement,CVBaseElement,IShapeElement,HierarchyElement,FrameElement,RenderableElement], CVShapeElement); CVShapeElement.prototype.initElement = RenderableDOMElement.prototype.initElement; CVShapeElement.prototype.transformHelper = {opacity:1,_opMdf:false}; CVShapeElement.prototype.dashResetter = []; CVShapeElement.prototype.createContent = function(){ this.searchShapes(this.shapesData,this.itemsData,this.prevViewData, true, []); }; CVShapeElement.prototype.createStyleElement = function(data, transforms) { var styleElem = { data: data, type: data.ty, preTransforms: this.transformsManager.addTransformSequence(transforms), transforms: [], elements: [], closed: data.hd === true }; var elementData = {}; if(data.ty == 'fl' || data.ty == 'st'){ elementData.c = PropertyFactory.getProp(this,data.c,1,255,this); if(!elementData.c.k){ styleElem.co = 'rgb('+bm_floor(elementData.c.v[0])+','+bm_floor(elementData.c.v[1])+','+bm_floor(elementData.c.v[2])+')'; } } else if (data.ty === 'gf' || data.ty === 'gs') { elementData.s = PropertyFactory.getProp(this,data.s,1,null,this); elementData.e = PropertyFactory.getProp(this,data.e,1,null,this); elementData.h = PropertyFactory.getProp(this,data.h||{k:0},0,0.01,this); elementData.a = PropertyFactory.getProp(this,data.a||{k:0},0,degToRads,this); elementData.g = new GradientProperty(this,data.g,this); } elementData.o = PropertyFactory.getProp(this,data.o,0,0.01,this); if(data.ty == 'st' || data.ty == 'gs') { styleElem.lc = this.lcEnum[data.lc] || 'round'; styleElem.lj = this.ljEnum[data.lj] || 'round'; if(data.lj == 1) { styleElem.ml = data.ml; } elementData.w = PropertyFactory.getProp(this,data.w,0,null,this); if(!elementData.w.k){ styleElem.wi = elementData.w.v; } if(data.d){ var d = new DashProperty(this,data.d,'canvas', this); elementData.d = d; if(!elementData.d.k){ styleElem.da = elementData.d.dashArray; styleElem.do = elementData.d.dashoffset[0]; } } } else { styleElem.r = data.r === 2 ? 'evenodd' : 'nonzero'; } this.stylesList.push(styleElem); elementData.style = styleElem; return elementData; }; CVShapeElement.prototype.createGroupElement = function(data) { var elementData = { it: [], prevViewData: [] }; return elementData; }; CVShapeElement.prototype.createTransformElement = function(data) { var elementData = { transform : { opacity: 1, _opMdf:false, key: this.transformsManager.getNewKey(), op: PropertyFactory.getProp(this,data.o,0,0.01,this), mProps: TransformPropertyFactory.getTransformProperty(this,data,this) } }; return elementData; }; CVShapeElement.prototype.createShapeElement = function(data) { var elementData = new CVShapeData(this, data, this.stylesList, this.transformsManager); this.shapes.push(elementData); this.addShapeToModifiers(elementData); return elementData; }; CVShapeElement.prototype.reloadShapes = function() { this._isFirstFrame = true; var i, len = this.itemsData.length; for (i = 0; i < len; i += 1) { this.prevViewData[i] = this.itemsData[i]; } this.searchShapes(this.shapesData,this.itemsData,this.prevViewData, true, []); len = this.dynamicProperties.length; for (i = 0; i < len; i += 1) { this.dynamicProperties[i].getValue(); } this.renderModifiers(); this.transformsManager.processSequences(this._isFirstFrame); }; CVShapeElement.prototype.addTransformToStyleList = function(transform) { var i, len = this.stylesList.length; for (i = 0; i < len; i += 1) { if(!this.stylesList[i].closed) { this.stylesList[i].transforms.push(transform); } } } CVShapeElement.prototype.removeTransformFromStyleList = function() { var i, len = this.stylesList.length; for (i = 0; i < len; i += 1) { if(!this.stylesList[i].closed) { this.stylesList[i].transforms.pop(); } } } CVShapeElement.prototype.closeStyles = function(styles) { var i, len = styles.length, j, jLen; for (i = 0; i < len; i += 1) { styles[i].closed = true; } } CVShapeElement.prototype.searchShapes = function(arr,itemsData, prevViewData, shouldRender, transforms){ var i, len = arr.length - 1; var j, jLen; var ownStyles = [], ownModifiers = [], processedPos, modifier, currentTransform; var ownTransforms = [].concat(transforms); for(i=len;i>=0;i-=1){ processedPos = this.searchProcessedElement(arr[i]); if(!processedPos){ arr[i]._shouldRender = shouldRender; } else { itemsData[i] = prevViewData[processedPos - 1]; } if(arr[i].ty == 'fl' || arr[i].ty == 'st'|| arr[i].ty == 'gf'|| arr[i].ty == 'gs'){ if(!processedPos){ itemsData[i] = this.createStyleElement(arr[i], ownTransforms); } else { itemsData[i].style.closed = false; } ownStyles.push(itemsData[i].style); }else if(arr[i].ty == 'gr'){ if(!processedPos){ itemsData[i] = this.createGroupElement(arr[i]); } else { jLen = itemsData[i].it.length; for(j=0;j=0;i-=1){ if(items[i].ty == 'tr'){ groupTransform = data[i].transform; this.renderShapeTransform(parentTransform, groupTransform); }else if(items[i].ty == 'sh' || items[i].ty == 'el' || items[i].ty == 'rc' || items[i].ty == 'sr'){ this.renderPath(items[i],data[i]); }else if(items[i].ty == 'fl'){ this.renderFill(items[i],data[i],groupTransform); }else if(items[i].ty == 'st'){ this.renderStroke(items[i],data[i],groupTransform); }else if(items[i].ty == 'gf' || items[i].ty == 'gs'){ this.renderGradientFill(items[i],data[i],groupTransform); }else if(items[i].ty == 'gr'){ this.renderShape(groupTransform,items[i].it,data[i].it); }else if(items[i].ty == 'tm'){ // } } if(isMain){ this.drawLayer(); } }; CVShapeElement.prototype.renderStyledShape = function(styledShape, shape){ if(this._isFirstFrame || shape._mdf || styledShape.transforms._mdf) { var shapeNodes = styledShape.trNodes; var paths = shape.paths; var i, len, j, jLen = paths._length; shapeNodes.length = 0; var groupTransformMat = styledShape.transforms.finalTransform; for (j = 0; j < jLen; j += 1) { var pathNodes = paths.shapes[j]; if(pathNodes && pathNodes.v){ len = pathNodes._length; for (i = 1; i < len; i += 1) { if (i === 1) { shapeNodes.push({ t: 'm', p: groupTransformMat.applyToPointArray(pathNodes.v[0][0], pathNodes.v[0][1], 0) }); } shapeNodes.push({ t: 'c', pts: groupTransformMat.applyToTriplePoints(pathNodes.o[i - 1], pathNodes.i[i], pathNodes.v[i]) }); } if (len === 1) { shapeNodes.push({ t: 'm', p: groupTransformMat.applyToPointArray(pathNodes.v[0][0], pathNodes.v[0][1], 0) }); } if (pathNodes.c && len) { shapeNodes.push({ t: 'c', pts: groupTransformMat.applyToTriplePoints(pathNodes.o[i - 1], pathNodes.i[0], pathNodes.v[0]) }); shapeNodes.push({ t: 'z' }); } } } styledShape.trNodes = shapeNodes; } } CVShapeElement.prototype.renderPath = function(pathData,itemData){ if(pathData.hd !== true && pathData._shouldRender) { var i, len = itemData.styledShapes.length; for (i = 0; i < len; i += 1) { this.renderStyledShape(itemData.styledShapes[i], itemData.sh); } } }; CVShapeElement.prototype.renderFill = function(styleData,itemData, groupTransform){ var styleElem = itemData.style; if (itemData.c._mdf || this._isFirstFrame) { styleElem.co = 'rgb(' + bm_floor(itemData.c.v[0]) + ',' + bm_floor(itemData.c.v[1]) + ',' + bm_floor(itemData.c.v[2]) + ')'; } if (itemData.o._mdf || groupTransform._opMdf || this._isFirstFrame) { styleElem.coOp = itemData.o.v * groupTransform.opacity; } }; CVShapeElement.prototype.renderGradientFill = function(styleData,itemData, groupTransform){ var styleElem = itemData.style; if(!styleElem.grd || itemData.g._mdf || itemData.s._mdf || itemData.e._mdf || (styleData.t !== 1 && (itemData.h._mdf || itemData.a._mdf))) { var ctx = this.globalData.canvasContext; var grd; var pt1 = itemData.s.v, pt2 = itemData.e.v; if (styleData.t === 1) { grd = ctx.createLinearGradient(pt1[0], pt1[1], pt2[0], pt2[1]); } else { var rad = Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) + Math.pow(pt1[1] - pt2[1], 2)); var ang = Math.atan2(pt2[1] - pt1[1], pt2[0] - pt1[0]); var percent = itemData.h.v >= 1 ? 0.99 : itemData.h.v <= -1 ? -0.99: itemData.h.v; var dist = rad * percent; var x = Math.cos(ang + itemData.a.v) * dist + pt1[0]; var y = Math.sin(ang + itemData.a.v) * dist + pt1[1]; var grd = ctx.createRadialGradient(x, y, 0, pt1[0], pt1[1], rad); } var i, len = styleData.g.p; var cValues = itemData.g.c; var opacity = 1; for (i = 0; i < len; i += 1){ if(itemData.g._hasOpacity && itemData.g._collapsable) { opacity = itemData.g.o[i*2 + 1]; } grd.addColorStop(cValues[i * 4] / 100,'rgba('+ cValues[i * 4 + 1] + ',' + cValues[i * 4 + 2] + ','+cValues[i * 4 + 3] + ',' + opacity + ')'); } styleElem.grd = grd; } styleElem.coOp = itemData.o.v*groupTransform.opacity; }; CVShapeElement.prototype.renderStroke = function(styleData,itemData, groupTransform){ var styleElem = itemData.style; var d = itemData.d; if(d && (d._mdf || this._isFirstFrame)){ styleElem.da = d.dashArray; styleElem.do = d.dashoffset[0]; } if(itemData.c._mdf || this._isFirstFrame){ styleElem.co = 'rgb('+bm_floor(itemData.c.v[0])+','+bm_floor(itemData.c.v[1])+','+bm_floor(itemData.c.v[2])+')'; } if(itemData.o._mdf || groupTransform._opMdf || this._isFirstFrame){ styleElem.coOp = itemData.o.v*groupTransform.opacity; } if(itemData.w._mdf || this._isFirstFrame){ styleElem.wi = itemData.w.v; } }; CVShapeElement.prototype.destroy = function(){ this.shapesData = null; this.globalData = null; this.canvasContext = null; this.stylesList.length = 0; this.itemsData.length = 0; }; function CVSolidElement(data, globalData, comp) { this.initElement(data,globalData,comp); } extendPrototype([BaseElement, TransformElement, CVBaseElement, HierarchyElement, FrameElement, RenderableElement], CVSolidElement); 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); // }; function CVEffects() { } CVEffects.prototype.renderFrame = function(){}; var animationManager = (function(){ var moduleOb = {}; var registeredAnimations = []; var initTime = 0; var len = 0; var playingAnimationsNum = 0; var _stopped = true; var _isFrozen = false; function removeElement(ev){ var i = 0; var animItem = ev.target; while(i=0;i-=1){ registeredAnimations[i].animation.destroy(animation); } } function searchAnimations(animationData, standalone, renderer){ var animElements = [].concat([].slice.call(document.getElementsByClassName('lottie')), [].slice.call(document.getElementsByClassName('bodymovin'))); var i, len = animElements.length; for(i=0;i=0;i-=1){ registeredAnimations[i].animation.destroy(animation); } } function searchAnimations(animationData, standalone, renderer){ throw new Error('Cannot access DOM from worker thread'); } function resize(){ var i; for(i=0;i this.animationData.op){ this.animationData.op = data.op; this.totalFrames = Math.floor(data.op - this.animationData.ip); } var layers = this.animationData.layers; var i, len = layers.length; var newLayers = data.layers; var j, jLen = newLayers.length; for(j=0;j this.timeCompleted){ this.currentFrame = this.timeCompleted; } this.trigger('enterFrame'); this.renderFrame(); }; AnimationItem.prototype.renderFrame = function () { if(this.isLoaded === false){ return; } this.renderer.renderFrame(this.currentFrame + this.firstFrame); }; AnimationItem.prototype.play = function (name) { if(name && this.name != name){ return; } if(this.isPaused === true){ this.isPaused = false; if(this._idle){ this._idle = false; this.trigger('_active'); } } }; AnimationItem.prototype.pause = function (name) { if(name && this.name != name){ return; } if(this.isPaused === false){ this.isPaused = true; this._idle = true; this.trigger('_idle'); } }; AnimationItem.prototype.togglePause = function (name) { if(name && this.name != name){ return; } if(this.isPaused === true){ this.play(); }else{ this.pause(); } }; AnimationItem.prototype.stop = function (name) { if(name && this.name != name){ return; } this.pause(); this.playCount = 0; this._completedLoop = false; this.setCurrentRawFrameValue(0); }; AnimationItem.prototype.goToAndStop = function (value, isFrame, name) { if(name && this.name != name){ return; } if(isFrame){ this.setCurrentRawFrameValue(value); }else{ this.setCurrentRawFrameValue(value * this.frameModifier); } this.pause(); }; AnimationItem.prototype.goToAndPlay = function (value, isFrame, name) { this.goToAndStop(value, isFrame, name); this.play(); }; AnimationItem.prototype.advanceTime = function (value) { if (this.isPaused === true || this.isLoaded === false) { return; } var nextValue = this.currentRawFrame + value * this.frameModifier; var _isComplete = false; // Checking if nextValue > totalFrames - 1 for addressing non looping and looping animations. // If animation won't loop, it should stop at totalFrames - 1. If it will loop it should complete the last frame and then loop. if (nextValue >= this.totalFrames - 1 && this.frameModifier > 0) { if (!this.loop || this.playCount === this.loop) { if (!this.checkSegments(nextValue > this.totalFrames ? nextValue % this.totalFrames : 0)) { _isComplete = true; nextValue = this.totalFrames - 1; } } else if (nextValue >= this.totalFrames) { this.playCount += 1; if (!this.checkSegments(nextValue % this.totalFrames)) { this.setCurrentRawFrameValue(nextValue % this.totalFrames); this._completedLoop = true; this.trigger('loopComplete'); } } else { this.setCurrentRawFrameValue(nextValue); } } else if(nextValue < 0) { if (!this.checkSegments(nextValue % this.totalFrames)) { if (this.loop && !(this.playCount-- <= 0 && this.loop !== true)) { this.setCurrentRawFrameValue(this.totalFrames + (nextValue % this.totalFrames)); if(!this._completedLoop) { this._completedLoop = true; } else { this.trigger('loopComplete'); } } else { _isComplete = true; nextValue = 0; } } } else { this.setCurrentRawFrameValue(nextValue); } if (_isComplete) { this.setCurrentRawFrameValue(nextValue); this.pause(); this.trigger('complete'); } }; AnimationItem.prototype.adjustSegment = function(arr, offset){ this.playCount = 0; if(arr[1] < arr[0]){ if(this.frameModifier > 0){ if(this.playSpeed < 0){ this.setSpeed(-this.playSpeed); } else { this.setDirection(-1); } } this.timeCompleted = this.totalFrames = arr[0] - arr[1]; this.firstFrame = arr[1]; this.setCurrentRawFrameValue(this.totalFrames - 0.001 - offset); } else if(arr[1] > arr[0]){ if(this.frameModifier < 0){ if(this.playSpeed < 0){ this.setSpeed(-this.playSpeed); } else { this.setDirection(1); } } this.timeCompleted = this.totalFrames = arr[1] - arr[0]; this.firstFrame = arr[0]; this.setCurrentRawFrameValue(0.001 + offset); } this.trigger('segmentStart'); }; AnimationItem.prototype.setSegment = function (init,end) { var pendingFrame = -1; if(this.isPaused) { if (this.currentRawFrame + this.firstFrame < init) { pendingFrame = init; } else if (this.currentRawFrame + this.firstFrame > end) { pendingFrame = end - init; } } this.firstFrame = init; this.timeCompleted = this.totalFrames = end - init; if(pendingFrame !== -1) { this.goToAndStop(pendingFrame,true); } }; AnimationItem.prototype.playSegments = function (arr, forceFlag) { if (forceFlag) { this.segments.length = 0; } if (typeof arr[0] === 'object') { var i, len = arr.length; for (i = 0; i < len; i += 1) { this.segments.push(arr[i]); } } else { this.segments.push(arr); } if (this.segments.length && forceFlag) { this.adjustSegment(this.segments.shift(), 0); } if (this.isPaused) { this.play(); } }; AnimationItem.prototype.resetSegments = function (forceFlag) { this.segments.length = 0; this.segments.push([this.animationData.ip,this.animationData.op]); //this.segments.push([this.animationData.ip*this.frameRate,Math.floor(this.animationData.op - this.animationData.ip+this.animationData.ip*this.frameRate)]); if (forceFlag) { this.checkSegments(0); } }; AnimationItem.prototype.checkSegments = function(offset) { if (this.segments.length) { this.adjustSegment(this.segments.shift(), offset); return true; } return false; }; AnimationItem.prototype.destroy = function (name) { if ((name && this.name != name) || !this.renderer) { return; } this.renderer.destroy(); this.imagePreloader.destroy(); this.trigger('destroy'); this._cbs = null; this.onEnterFrame = this.onLoopComplete = this.onComplete = this.onSegmentStart = this.onDestroy = null; this.renderer = null; }; AnimationItem.prototype.setCurrentRawFrameValue = function(value){ this.currentRawFrame = value; this.gotoFrame(); }; AnimationItem.prototype.setSpeed = function (val) { this.playSpeed = val; this.updaFrameModifier(); }; AnimationItem.prototype.setDirection = function (val) { this.playDirection = val < 0 ? -1 : 1; this.updaFrameModifier(); }; AnimationItem.prototype.updaFrameModifier = function () { this.frameModifier = this.frameMult * this.playSpeed * this.playDirection; }; AnimationItem.prototype.getPath = function () { return this.path; }; AnimationItem.prototype.getAssetsPath = function (assetData) { var path = ''; if(assetData.e) { path = assetData.p; } else if(this.assetsPath){ var imagePath = assetData.p; if(imagePath.indexOf('images/') !== -1){ imagePath = imagePath.split('/')[1]; } path = this.assetsPath + imagePath; } else { path = this.path; path += assetData.u ? assetData.u : ''; path += assetData.p; } return path; }; AnimationItem.prototype.getAssetData = function (id) { var i = 0, len = this.assets.length; while (i < len) { if(id == this.assets[i].id){ return this.assets[i]; } i += 1; } }; AnimationItem.prototype.hide = function () { this.renderer.hide(); }; AnimationItem.prototype.show = function () { this.renderer.show(); }; AnimationItem.prototype.getDuration = function (isFrame) { return isFrame ? this.totalFrames : this.totalFrames / this.frameRate; }; AnimationItem.prototype.trigger = function(name){ if(this._cbs && this._cbs[name]){ switch(name){ case 'enterFrame': this.triggerEvent(name,new BMEnterFrameEvent(name,this.currentFrame,this.totalFrames,this.frameModifier)); break; case 'loopComplete': this.triggerEvent(name,new BMCompleteLoopEvent(name,this.loop,this.playCount,this.frameMult)); break; case 'complete': this.triggerEvent(name,new BMCompleteEvent(name,this.frameMult)); break; case 'segmentStart': this.triggerEvent(name,new BMSegmentStartEvent(name,this.firstFrame,this.totalFrames)); break; case 'destroy': this.triggerEvent(name,new BMDestroyEvent(name,this)); break; default: this.triggerEvent(name); } } if(name === 'enterFrame' && this.onEnterFrame){ this.onEnterFrame.call(this,new BMEnterFrameEvent(name,this.currentFrame,this.totalFrames,this.frameMult)); } if(name === 'loopComplete' && this.onLoopComplete){ this.onLoopComplete.call(this,new BMCompleteLoopEvent(name,this.loop,this.playCount,this.frameMult)); } if(name === 'complete' && this.onComplete){ this.onComplete.call(this,new BMCompleteEvent(name,this.frameMult)); } if(name === 'segmentStart' && this.onSegmentStart){ this.onSegmentStart.call(this,new BMSegmentStartEvent(name,this.firstFrame,this.totalFrames)); } if(name === 'destroy' && this.onDestroy){ this.onDestroy.call(this,new BMDestroyEvent(name,this)); } }; AnimationItem.prototype.setParams = function(params) { if(params.context){ this.context = params.context; } var animType = params.animType ? params.animType : params.renderer ? params.renderer : 'svg'; switch(animType){ case 'canvas': this.renderer = new CanvasRenderer(this, params.rendererSettings); break; default: throw new Error('Only canvas renderer is supported when using worker.'); break; } this.renderer.setProjectInterface(this.projectInterface); this.animType = animType; if(params.loop === '' || params.loop === null){ }else if(params.loop === false){ this.loop = false; }else if(params.loop === true){ this.loop = true; }else{ this.loop = parseInt(params.loop); } this.autoplay = 'autoplay' in params ? params.autoplay : true; this.name = params.name ? params.name : ''; this.autoloadSegments = params.hasOwnProperty('autoloadSegments') ? params.autoloadSegments : true; this.assetsPath = null; if(params.animationData){ this.configAnimation(params.animationData); }else if(params.path){ throw new Error('Canvas worker renderer cannot load animation from url'); } }; AnimationItem.prototype.setData = function (wrapper, animationData) { throw new Error('Cannot set data on wrapper for canvas worker renderer'); }; AnimationItem.prototype.includeLayers = function(data) { if(data.op > this.animationData.op){ this.animationData.op = data.op; this.totalFrames = Math.floor(data.op - this.animationData.ip); } var layers = this.animationData.layers; var i, len = layers.length; var newLayers = data.layers; var j, jLen = newLayers.length; for(j=0;j max){ var mm = max; max = min; min = mm; } return Math.min(Math.max(num, min), max); } function radiansToDegrees(val) { return val/degToRads; } var radians_to_degrees = radiansToDegrees; function degreesToRadians(val) { return val*degToRads; } var degrees_to_radians = radiansToDegrees; var helperLengthArray = [0,0,0,0,0,0]; function length(arr1, arr2) { if (typeof arr1 === 'number' || arr1 instanceof Number) { arr2 = arr2 || 0; return Math.abs(arr1 - arr2); } if(!arr2) { arr2 = helperLengthArray; } var i, len = Math.min(arr1.length, arr2.length); var addedLength = 0; for (i = 0; i < len; i += 1) { addedLength += Math.pow(arr2[i] - arr1[i], 2); } return Math.sqrt(addedLength); } function normalize(vec) { return div(vec, length(vec)); } function rgbToHsl(val) { var r = val[0]; var g = val[1]; var b = val[2]; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if(max == min){ h = s = 0; // achromatic }else{ var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch(max){ case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h, s, l,val[3]]; } function hue2rgb(p, q, t){ if(t < 0) t += 1; if(t > 1) t -= 1; if(t < 1/6) return p + (q - p) * 6 * t; if(t < 1/2) return q; if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; } function hslToRgb(val){ var h = val[0]; var s = val[1]; var l = val[2]; var r, g, b; if(s === 0){ r = g = b = l; // achromatic }else{ var q = l < 0.5 ? l * (1 + s) : l + s - l * s; var p = 2 * l - q; r = hue2rgb(p, q, h + 1/3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1/3); } return [r, g , b, val[3]]; } function linear(t, tMin, tMax, value1, value2){ if(value1 === undefined || value2 === undefined){ value1 = tMin; value2 = tMax; tMin = 0; tMax = 1; } if(tMax < tMin) { var _tMin = tMax; tMax = tMin; tMin = _tMin; } if(t <= tMin) { return value1; }else if(t >= tMax){ return value2; } var perc = tMax === tMin ? 0 : (t-tMin)/(tMax-tMin); if(!value1.length){ return value1 + (value2-value1)*perc; } var i, len = value1.length; var arr = createTypedArray('float32', len); for(i=0;i1){ for(j=0;j 1 ? 1 : t < 0 ? 0 : t; var mult = fn(t); if($bm_isInstanceOfArray(val1)) { var i, len = val1.length; var arr = createTypedArray('float32', len); for (i = 0; i < len; i += 1) { arr[i] = (val2[i] - val1[i]) * mult + val1[i]; } return arr; } else { return (val2 - val1) * mult + val1; } } function nearestKey(time){ var i, len = data.k.length,index,keyTime; if(!data.k.length || typeof(data.k[0]) === 'number'){ index = 0; keyTime = 0; } else { index = -1; time *= elem.comp.globalData.frameRate; if (time < data.k[0].t) { index = 1; keyTime = data.k[0].t; } else { for(i=0;idata.k[i].t && time data.k[i+1].t - time){ index = i + 2; keyTime = data.k[i+1].t; } else { index = i + 1; keyTime = data.k[i].t; } break; } } if(index === -1){ index = i + 1; keyTime = data.k[i].t; } } } var ob = {}; ob.index = index; ob.time = keyTime/elem.comp.globalData.frameRate; return ob; } function key(ind){ var ob, i, len; if(!data.k.length || typeof(data.k[0]) === 'number'){ throw new Error('The property has no keyframe at index ' + ind); } ind -= 1; ob = { time: data.k[ind].t/elem.comp.globalData.frameRate, value: [] }; var arr; if(ind === data.k.length - 1 && !data.k[ind].h){ arr = (data.k[ind].s || data.k[ind].s === 0) ? data.k[ind-1].s : data.k[ind].e; }else{ arr = data.k[ind].s; } len = arr.length; for(i=0;i keyframes.length - 1){ duration = keyframes.length - 1; } firstKeyFrame = keyframes[keyframes.length - 1 - duration].t; cycleDuration = lastKeyFrame - firstKeyFrame; } else { if(!duration){ cycleDuration = Math.max(0,lastKeyFrame - this.elem.data.ip); } else { cycleDuration = Math.abs(lastKeyFrame - elem.comp.globalData.frameRate*duration); } firstKeyFrame = lastKeyFrame - cycleDuration; } var i, len, ret; if(type === 'pingpong') { var iterations = Math.floor((currentFrame - firstKeyFrame)/cycleDuration); if(iterations % 2 !== 0){ return this.getValueAtTime(((cycleDuration - (currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0); } } else if(type === 'offset'){ var initV = this.getValueAtTime(firstKeyFrame / this.comp.globalData.frameRate, 0); var endV = this.getValueAtTime(lastKeyFrame / this.comp.globalData.frameRate, 0); var current = this.getValueAtTime(((currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame) / this.comp.globalData.frameRate, 0); var repeats = Math.floor((currentFrame - firstKeyFrame)/cycleDuration); if(this.pv.length){ ret = new Array(initV.length); len = ret.length; for(i=0;i=firstKeyFrame){ return this.pv; }else{ var cycleDuration, lastKeyFrame; if(!durationFlag){ if(!duration || duration > keyframes.length - 1){ duration = keyframes.length - 1; } lastKeyFrame = keyframes[duration].t; cycleDuration = lastKeyFrame - firstKeyFrame; } else { if(!duration){ cycleDuration = Math.max(0,this.elem.data.op - firstKeyFrame); } else { cycleDuration = Math.abs(elem.comp.globalData.frameRate*duration); } lastKeyFrame = firstKeyFrame + cycleDuration; } var i, len, ret; if(type === 'pingpong') { var iterations = Math.floor((firstKeyFrame - currentFrame)/cycleDuration); if(iterations % 2 === 0){ return this.getValueAtTime((((firstKeyFrame - currentFrame)%cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0); } } else if(type === 'offset'){ var initV = this.getValueAtTime(firstKeyFrame / this.comp.globalData.frameRate, 0); var endV = this.getValueAtTime(lastKeyFrame / this.comp.globalData.frameRate, 0); var current = this.getValueAtTime((cycleDuration - (firstKeyFrame - currentFrame)%cycleDuration + firstKeyFrame) / this.comp.globalData.frameRate, 0); var repeats = Math.floor((firstKeyFrame - currentFrame)/cycleDuration)+1; if(this.pv.length){ ret = new Array(initV.length); len = ret.length; for(i=0;i 1 ? (endFrame - initFrame) / (samples - 1) : 1; var i = 0, j = 0; var value; if (this.pv.length) { value = createTypedArray('float32', this.pv.length); } else { value = 0; } var sampleValue; while (i < samples) { sampleValue = this.getValueAtTime(initFrame + i * sampleFrequency); if(this.pv.length) { for (j = 0; j < this.pv.length; j += 1) { value[j] += sampleValue[j]; } } else { value += sampleValue; } i += 1; } if(this.pv.length) { for (j = 0; j < this.pv.length; j += 1) { value[j] /= samples; } } else { value /= samples; } return value; } function getValueAtTime(frameNum) { frameNum *= this.elem.globalData.frameRate; frameNum -= this.offsetTime; if(frameNum !== this._cachingAtTime.lastFrame) { this._cachingAtTime.lastIndex = this._cachingAtTime.lastFrame < frameNum ? this._cachingAtTime.lastIndex : 0; this._cachingAtTime.value = this.interpolateValue(frameNum, this._cachingAtTime); this._cachingAtTime.lastFrame = frameNum; } return this._cachingAtTime.value; } function getTransformValueAtTime(time) { console.warn('Transform at time not supported'); } function getTransformStaticValueAtTime(time) { } var getTransformProperty = TransformPropertyFactory.getTransformProperty; TransformPropertyFactory.getTransformProperty = function(elem, data, container) { var prop = getTransformProperty(elem, data, container); if(prop.dynamicProperties.length) { prop.getValueAtTime = getTransformValueAtTime.bind(prop); } else { prop.getValueAtTime = getTransformStaticValueAtTime.bind(prop); } prop.setGroupProperty = expressionHelpers.setGroupProperty; return prop; }; var propertyGetProp = PropertyFactory.getProp; PropertyFactory.getProp = function(elem,data,type, mult, container){ var prop = propertyGetProp(elem,data,type, mult, container); //prop.getVelocityAtTime = getVelocityAtTime; //prop.loopOut = loopOut; //prop.loopIn = loopIn; if(prop.kf){ prop.getValueAtTime = expressionHelpers.getValueAtTime.bind(prop); } else { prop.getValueAtTime = expressionHelpers.getStaticValueAtTime.bind(prop); } prop.setGroupProperty = expressionHelpers.setGroupProperty; prop.loopOut = loopOut; prop.loopIn = loopIn; prop.smooth = smooth; prop.getVelocityAtTime = expressionHelpers.getVelocityAtTime.bind(prop); prop.getSpeedAtTime = expressionHelpers.getSpeedAtTime.bind(prop); prop.numKeys = data.a === 1 ? data.k.length : 0; prop.propertyIndex = data.ix; var value = 0; if(type !== 0) { value = createTypedArray('float32', data.a === 1 ? data.k[0].s.length : data.k.length); } prop._cachingAtTime = { lastFrame: initialDefaultFrame, lastIndex: 0, value: value }; expressionHelpers.searchExpressions(elem,data,prop); if(prop.k){ container.addDynamicProperty(prop); } return prop; }; function getShapeValueAtTime(frameNum) { //For now this caching object is created only when needed instead of creating it when the shape is initialized. if (!this._cachingAtTime) { this._cachingAtTime = { shapeValue: shape_pool.clone(this.pv), lastIndex: 0, lastTime: initialDefaultFrame }; } frameNum *= this.elem.globalData.frameRate; frameNum -= this.offsetTime; if(frameNum !== this._cachingAtTime.lastTime) { this._cachingAtTime.lastIndex = this._cachingAtTime.lastTime < frameNum ? this._caching.lastIndex : 0; this._cachingAtTime.lastTime = frameNum; this.interpolateShape(frameNum, this._cachingAtTime.shapeValue, this._cachingAtTime); } return this._cachingAtTime.shapeValue; } var ShapePropertyConstructorFunction = ShapePropertyFactory.getConstructorFunction(); var KeyframedShapePropertyConstructorFunction = ShapePropertyFactory.getKeyframedConstructorFunction(); function ShapeExpressions(){} ShapeExpressions.prototype = { vertices: function(prop, time){ if (this.k) { this.getValue(); } var shapePath = this.v; if(time !== undefined) { shapePath = this.getValueAtTime(time, 0); } var i, len = shapePath._length; var vertices = shapePath[prop]; var points = shapePath.v; var arr = createSizedArray(len); for(i = 0; i < len; i += 1) { if(prop === 'i' || prop === 'o') { arr[i] = [vertices[i][0] - points[i][0], vertices[i][1] - points[i][1]]; } else { arr[i] = [vertices[i][0], vertices[i][1]]; } } return arr; }, points: function(time){ return this.vertices('v', time); }, inTangents: function(time){ return this.vertices('i', time); }, outTangents: function(time){ return this.vertices('o', time); }, isClosed: function(){ return this.v.c; }, pointOnPath: function(perc, time){ var shapePath = this.v; if(time !== undefined) { shapePath = this.getValueAtTime(time, 0); } if(!this._segmentsLength) { this._segmentsLength = bez.getSegmentsLength(shapePath); } var segmentsLength = this._segmentsLength; var lengths = segmentsLength.lengths; var lengthPos = segmentsLength.totalLength * perc; var i = 0, len = lengths.length; var j = 0, jLen; var accumulatedLength = 0, pt; while(i < len) { if(accumulatedLength + lengths[i].addedLength > lengthPos) { var initIndex = i; var endIndex = (shapePath.c && i === len - 1) ? 0 : i + 1; var segmentPerc = (lengthPos - accumulatedLength)/lengths[i].addedLength; pt = bez.getPointInSegment(shapePath.v[initIndex], shapePath.v[endIndex], shapePath.o[initIndex], shapePath.i[endIndex], segmentPerc, lengths[i]); break; } else { accumulatedLength += lengths[i].addedLength; } i += 1; } if(!pt){ pt = shapePath.c ? [shapePath.v[0][0],shapePath.v[0][1]]:[shapePath.v[shapePath._length-1][0],shapePath.v[shapePath._length-1][1]]; } return pt; }, vectorOnPath: function(perc, time, vectorType){ //perc doesn't use triple equality because it can be a Number object as well as a primitive. perc = perc == 1 ? this.v.c ? 0 : 0.999 : perc; var pt1 = this.pointOnPath(perc, time); var pt2 = this.pointOnPath(perc + 0.001, time); var xLength = pt2[0] - pt1[0]; var yLength = pt2[1] - pt1[1]; var magnitude = Math.sqrt(Math.pow(xLength,2) + Math.pow(yLength,2)); var unitVector = vectorType === 'tangent' ? [xLength/magnitude, yLength/magnitude] : [-yLength/magnitude, xLength/magnitude]; return unitVector; }, tangentOnPath: function(perc, time){ return this.vectorOnPath(perc, time, 'tangent'); }, normalOnPath: function(perc, time){ return this.vectorOnPath(perc, time, 'normal'); }, setGroupProperty: expressionHelpers.setGroupProperty, getValueAtTime: expressionHelpers.getStaticValueAtTime }; extendPrototype([ShapeExpressions], ShapePropertyConstructorFunction); extendPrototype([ShapeExpressions], KeyframedShapePropertyConstructorFunction); KeyframedShapePropertyConstructorFunction.prototype.getValueAtTime = getShapeValueAtTime; KeyframedShapePropertyConstructorFunction.prototype.initiateExpression = ExpressionManager.initiateExpression; var propertyGetShapeProp = ShapePropertyFactory.getShapeProp; ShapePropertyFactory.getShapeProp = function(elem,data,type, arr, trims){ var prop = propertyGetShapeProp(elem,data,type, arr, trims); prop.propertyIndex = data.ix; prop.lock = false; if(type === 3){ expressionHelpers.searchExpressions(elem,data.pt,prop); } else if(type === 4){ expressionHelpers.searchExpressions(elem,data.ks,prop); } if(prop.k){ elem.addDynamicProperty(prop); } return prop; }; }()); (function addDecorator() { function searchExpressions(){ if(this.data.d.x){ this.calculateExpression = ExpressionManager.initiateExpression.bind(this)(this.elem,this.data.d,this); this.addEffect(this.getExpressionValue.bind(this)); return true; } } TextProperty.prototype.getExpressionValue = function(currentValue, text) { var newValue = this.calculateExpression(text); if(currentValue.t !== newValue) { var newData = {}; this.copyData(newData, currentValue); newData.t = newValue.toString(); newData.__complete = false; return newData; } return currentValue; } TextProperty.prototype.searchProperty = function(){ var isKeyframed = this.searchKeyframes(); var hasExpressions = this.searchExpressions(); this.kf = isKeyframed || hasExpressions; return this.kf; }; TextProperty.prototype.searchExpressions = searchExpressions; }()); var ShapeExpressionInterface = (function(){ function iterateElements(shapes,view, propertyGroup){ var arr = []; var i, len = shapes ? shapes.length : 0; for(i=0;i 1) { defaultCurveSegments = value; } if (defaultCurveSegments >= 50) { roundValues(false); } else { roundValues(true); } } lottiejs.play = animationManager.play; lottiejs.pause = animationManager.pause; lottiejs.togglePause = animationManager.togglePause; lottiejs.setSpeed = animationManager.setSpeed; lottiejs.setDirection = animationManager.setDirection; lottiejs.stop = animationManager.stop; lottiejs.registerAnimation = animationManager.registerAnimation; lottiejs.loadAnimation = loadAnimation; lottiejs.resize = animationManager.resize; lottiejs.goToAndStop = animationManager.goToAndStop; lottiejs.destroy = animationManager.destroy; lottiejs.setQuality = setQuality; lottiejs.freeze = animationManager.freeze; lottiejs.unfreeze = animationManager.unfreeze; lottiejs.getRegisteredAnimations = animationManager.getRegisteredAnimations; lottiejs.version = '5.5.2'; var renderer = ''; return lottiejs; })({}); /** * Code below is added to handle the messages sent to the worker thread. Its a * a wrapper that manages animation control for each animation. */ /** * An instance of the currently running lottie animation. * @type {?AnimationItem} */ var currentAnimation = null; /** * Events sent back to the parent thread. */ var events = { INITIALIZED: 'initialized', // Send when the animation was successfully // initialized. RESIZED: 'resized', // Sent when the animation has been resized. PLAYING: 'playing', // Send when the animation started playing. PAUSED: 'paused', // Sent when the animation has paused. STOPPED: 'stopped', // Sent when the animation has stopped. }; /** * Returns the size of the canvas on which the current animation is running on. * @return {Object} Returns the current size of canvas */ function getCurrentCanvasSize() { var canvas = currentAnimation.renderer.canvasContext.canvas; return { height: canvas.height, width: canvas.width }; } /** * Informs the parent thread that the canvas has been resized and also sends the * new size. */ function sendResizeEvent() { var canvas = currentAnimation.renderer.canvasContext.canvas; postMessage({ name: events.RESIZED, size: getCurrentCanvasSize() }); }; /** * Informs the parent thread that the animation is playing. */ function sendPlayEvent() { postMessage({name: events.PLAYING}); } /** * Informs the parent thread that the animation is paused. */ function sendPauseEvent() { postMessage({name: events.PAUSED}); } /** * Informs the parent thread that the animation is stopped. */ function sendStopEvent() { postMessage({name: events.STOPPED}); } /** * Informs the parent thread that the animation has finished initializing. */ function sendInitializedEvent() { var canvas = currentAnimation.renderer.canvasContext.canvas; postMessage({ name: events.INITIALIZED, success: currentAnimation.isLoaded }) } /** * Initializes the animation using the animation data sent from the parent * thread along with the init parameters. If an animation is already initialized * this is a no-op. * @param {JSON} animationData JSON data for the animation. * @param {Object} initParams Parameters to initialize the animation with. * @param {OffscreenCanvas} canvas The offsccreen canvas that will display the * animation. */ function initAnimation(animationData, initParams, canvas) { if (!animationData || !initParams) { return; } var ctx = canvas.getContext("2d"); currentAnimation = lottiejs.loadAnimation({ renderer: 'canvas', loop: initParams.loop, autoplay: initParams.autoplay, animationData: animationData, rendererSettings: { context: ctx, scaleMode: 'noScale', clearCanvas: true } }); sendInitializedEvent(); // Play the animation if its not already playing. if (initParams.autoplay) { currentAnimation.play(); } if (currentAnimation.isLoaded && !currentAnimation.isPaused) { sendPlayEvent(); } } /** * Updates the canvas draw size. If an animation is already initialized or * playing, this would update the canvas for that as well. * @param {OffscreenCanvas} canvas Draw size for this canvas is updated. * @param {Object} size Draw size to update the canvas to. */ function updateCanvasSize(canvas, size) { if (!size || !canvas) { return; } if (size.height > 0 && size.width > 0) { canvas.height = size.height; canvas.width = size.width; } // If an animation is already initialized then update its canvas size. if (currentAnimation) { currentAnimation.resize(); sendResizeEvent(); } } /** * Updates the animation state to play or pause. Informs the parent thread if * the state has changed. * @param {Object} control Has information about playing or * pausing the animation. * @param {HTMLCanvasElement} canvas the canvas on which the animation is drawn. */ function updateAnimationState(control, canvas) { if (!control || !currentAnimation) { return; } if (control.stop) { currentAnimation.stop(); canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); sendStopEvent(); return; } if (control.play && currentAnimation.isPaused) { currentAnimation.play(); sendPlayEvent(); } else if (!control.play && !currentAnimation.isPaused) { currentAnimation.pause(); sendPauseEvent(); } } onmessage = function(evt) { if (!evt || !evt.data) return; var canvas = null; if (currentAnimation) { canvas = currentAnimation.renderer.canvasContext.canvas; } else if (evt.data.canvas) { canvas = evt.data.canvas; } else { return; } // Stop and clear the current animation to initialize a new one with the // provided animation data. if (currentAnimation && evt.data.animationData) { currentAnimation.stop(); currentAnimation = null; } updateCanvasSize(canvas, evt.data.drawSize); initAnimation(evt.data.animationData, evt.data.params, canvas); updateAnimationState(evt.data.control, canvas); };