| var Clip = require("./Clip"); | 
|   | 
| var _util = require("../core/util"); | 
|   | 
| var isArrayLike = _util.isArrayLike; | 
|   | 
| var color = require("../tool/color"); | 
|   | 
| var arraySlice = Array.prototype.slice; | 
| /** | 
|  * @param {Object} target | 
|  * @param {string} propName | 
|  * @param {Array.<Object>} keyframes | 
|  *        [{ | 
|  *            time: number, | 
|  *            value: number | color string | Array.<number> | Array.<Array.<number>> | 
|  *        }, ...] | 
|  *        [Caveat]: | 
|  *        (1) The order should ensured by time. | 
|  *        (2) If `value` is `Array`, it must not be shared outside (espaciall el.shape, el.style), | 
|  *        in case that it be modified outside and cause incorrect interpolate result. | 
|  * @param {string} easing | 
|  * @param {boolean} [delay=false] | 
|  * @param {boolean} [loop=false] | 
|  * @param {boolean} [forceAnimate=false] | 
|  * @param {Function} [getter=defaultGetter] | 
|  * @param {Function} [setter=defaultSetter] | 
|  * @return {module:zrender/animation/Clip} clip | 
|  */ | 
|   | 
| function createTrackClip(target, propName, keyframes, easing, delay, loop, forceAnimate, getter, setter) { | 
|   var useSpline = easing === 'spline'; | 
|   getter = getter || defaultGetter; | 
|   setter = setter || defaultSetter; | 
|   var trackLen = keyframes.length; | 
|   | 
|   if (!trackLen) { | 
|     return; | 
|   } // Guess data type | 
|   | 
|   | 
|   var firstVal = keyframes[0].value; | 
|   var isValueArray = isArrayLike(firstVal); | 
|   var isValueColor = false; | 
|   var isValueString = false; // For vertices morphing | 
|   | 
|   var arrDim = isValueArray ? getArrayDim(keyframes) : 0; | 
|   var trackMaxTime; | 
|   trackMaxTime = keyframes[trackLen - 1].time; // Percents of each keyframe | 
|   | 
|   var kfPercents = []; // Value of each keyframe | 
|   | 
|   var kfValues = []; | 
|   var prevValue = keyframes[0].value; | 
|   var isAllValueEqual = true; | 
|   | 
|   for (var i = 0; i < trackLen; i++) { | 
|     kfPercents.push(keyframes[i].time / trackMaxTime); // Assume value is a color when it is a string | 
|   | 
|     var value = keyframes[i].value; // Check if value is equal, deep check if value is array | 
|   | 
|     if (!(isValueArray && isArraySame(value, prevValue, arrDim) || !isValueArray && value === prevValue)) { | 
|       isAllValueEqual = false; | 
|     } | 
|   | 
|     prevValue = value; // Try converting a string to a color array | 
|   | 
|     if (typeof value === 'string') { | 
|       var colorArray = color.parse(value); | 
|   | 
|       if (colorArray) { | 
|         value = colorArray; | 
|         isValueColor = true; | 
|       } else { | 
|         isValueString = true; | 
|       } | 
|     } | 
|   | 
|     kfValues.push(value); | 
|   } | 
|   | 
|   if (!forceAnimate && isAllValueEqual) { | 
|     return; | 
|   } | 
|   | 
|   var lastValue = kfValues[trackLen - 1]; // Polyfill array and NaN value | 
|   | 
|   for (var i = 0; i < trackLen - 1; i++) { | 
|     if (isValueArray) { | 
|       fillArr(kfValues[i], lastValue, arrDim); | 
|     } else { | 
|       if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) { | 
|         kfValues[i] = lastValue; | 
|       } | 
|     } | 
|   } | 
|   | 
|   isValueArray && fillArr(getter(target, propName), lastValue, arrDim); // Cache the key of last frame to speed up when | 
|   // animation playback is sequency | 
|   | 
|   var lastFrame = 0; | 
|   var lastFramePercent = 0; | 
|   var start; | 
|   var w; | 
|   var p0; | 
|   var p1; | 
|   var p2; | 
|   var p3; | 
|   | 
|   if (isValueColor) { | 
|     var rgba = [0, 0, 0, 0]; | 
|   } | 
|   | 
|   function hanleFrame(target, percent) { | 
|     // Find the range keyframes | 
|     // kf1-----kf2---------current--------kf3 | 
|     // find kf2 and kf3 and do interpolation | 
|     var frame; // In the easing function like elasticOut, percent may less than 0 | 
|   | 
|     if (percent < 0) { | 
|       frame = 0; | 
|     } else if (percent < lastFramePercent) { | 
|       // Start from next key | 
|       // PENDING start from lastFrame ? | 
|       start = Math.min(lastFrame + 1, trackLen - 1); | 
|   | 
|       for (frame = start; frame >= 0; frame--) { | 
|         if (kfPercents[frame] <= percent) { | 
|           break; | 
|         } | 
|       } // PENDING really need to do this ? | 
|   | 
|   | 
|       frame = Math.min(frame, trackLen - 2); | 
|     } else { | 
|       for (frame = lastFrame; frame < trackLen; frame++) { | 
|         if (kfPercents[frame] > percent) { | 
|           break; | 
|         } | 
|       } | 
|   | 
|       frame = Math.min(frame - 1, trackLen - 2); | 
|     } | 
|   | 
|     lastFrame = frame; | 
|     lastFramePercent = percent; | 
|     var range = kfPercents[frame + 1] - kfPercents[frame]; | 
|   | 
|     if (range === 0) { | 
|       return; | 
|     } else { | 
|       w = (percent - kfPercents[frame]) / range; | 
|     } | 
|   | 
|     if (useSpline) { | 
|       p1 = kfValues[frame]; | 
|       p0 = kfValues[frame === 0 ? frame : frame - 1]; | 
|       p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1]; | 
|       p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2]; | 
|   | 
|       if (isValueArray) { | 
|         catmullRomInterpolateArray(p0, p1, p2, p3, w, w * w, w * w * w, getter(target, propName), arrDim); | 
|       } else { | 
|         var value; | 
|   | 
|         if (isValueColor) { | 
|           value = catmullRomInterpolateArray(p0, p1, p2, p3, w, w * w, w * w * w, rgba, 1); | 
|           value = rgba2String(rgba); | 
|         } else if (isValueString) { | 
|           // String is step(0.5) | 
|           return interpolateString(p1, p2, w); | 
|         } else { | 
|           value = catmullRomInterpolate(p0, p1, p2, p3, w, w * w, w * w * w); | 
|         } | 
|   | 
|         setter(target, propName, value); | 
|       } | 
|     } else { | 
|       if (isValueArray) { | 
|         interpolateArray(kfValues[frame], kfValues[frame + 1], w, getter(target, propName), arrDim); | 
|       } else { | 
|         var value; | 
|   | 
|         if (isValueColor) { | 
|           interpolateArray(kfValues[frame], kfValues[frame + 1], w, rgba, 1); | 
|           value = rgba2String(rgba); | 
|         } else if (isValueString) { | 
|           // String is step(0.5) | 
|           return interpolateString(kfValues[frame], kfValues[frame + 1], w); | 
|         } else { | 
|           value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w); | 
|         } | 
|   | 
|         if (target.aaaa != null) { | 
|           console.log(target.uuid, value, propName); | 
|         } | 
|   | 
|         setter(target, propName, value); | 
|       } | 
|     } | 
|   } | 
|   | 
|   var clip = new Clip({ | 
|     target: target, | 
|     life: trackMaxTime, | 
|     loop: loop, | 
|     delay: delay, | 
|     onframe: hanleFrame | 
|   }); | 
|   | 
|   if (easing && easing !== 'spline') { | 
|     clip.easing = easing; | 
|   } | 
|   | 
|   return clip; | 
| } | 
|   | 
| function getArrayDim(keyframes) { | 
|   var lastValue = keyframes[keyframes.length - 1].value; | 
|   return isArrayLike(lastValue && lastValue[0]) ? 2 : 1; | 
| } | 
| /** | 
|  * @param  {Array} arr0 | 
|  * @param  {Array} arr1 | 
|  * @param  {number} arrDim | 
|  * @return {boolean} | 
|  */ | 
|   | 
|   | 
| function isArraySame(arr0, arr1, arrDim) { | 
|   if (arr0 === arr1) { | 
|     return true; | 
|   } | 
|   | 
|   var len = arr0.length; | 
|   | 
|   if (len !== arr1.length) { | 
|     return false; | 
|   } | 
|   | 
|   if (arrDim === 1) { | 
|     for (var i = 0; i < len; i++) { | 
|       if (arr0[i] !== arr1[i]) { | 
|         return false; | 
|       } | 
|     } | 
|   } else { | 
|     var len2 = arr0[0].length; | 
|   | 
|     for (var i = 0; i < len; i++) { | 
|       for (var j = 0; j < len2; j++) { | 
|         if (arr0[i][j] !== arr1[i][j]) { | 
|           return false; | 
|         } | 
|       } | 
|     } | 
|   } | 
|   | 
|   return true; | 
| } // arr0 is source array, arr1 is target array. | 
| // Do some preprocess to avoid error happened when interpolating from arr0 to arr1 | 
|   | 
|   | 
| function fillArr(arr0, arr1, arrDim) { | 
|   var arr0Len = arr0.length; | 
|   var arr1Len = arr1.length; | 
|   | 
|   if (arr0Len !== arr1Len) { | 
|     // FIXME Not work for TypedArray | 
|     var isPreviousLarger = arr0Len > arr1Len; | 
|   | 
|     if (isPreviousLarger) { | 
|       // Cut the previous | 
|       arr0.length = arr1Len; | 
|     } else { | 
|       // Fill the previous | 
|       for (var i = arr0Len; i < arr1Len; i++) { | 
|         arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])); | 
|       } | 
|     } | 
|   } // Handling NaN value | 
|   | 
|   | 
|   var len2 = arr0[0] && arr0[0].length; | 
|   | 
|   for (var i = 0; i < arr0.length; i++) { | 
|     if (arrDim === 1) { | 
|       if (isNaN(arr0[i])) { | 
|         arr0[i] = arr1[i]; | 
|       } | 
|     } else { | 
|       for (var j = 0; j < len2; j++) { | 
|         if (isNaN(arr0[i][j])) { | 
|           arr0[i][j] = arr1[i][j]; | 
|         } | 
|       } | 
|     } | 
|   } | 
| } | 
| /** | 
|  * Catmull Rom interpolate array | 
|  * @param  {Array} p0 | 
|  * @param  {Array} p1 | 
|  * @param  {Array} p2 | 
|  * @param  {Array} p3 | 
|  * @param  {number} t | 
|  * @param  {number} t2 | 
|  * @param  {number} t3 | 
|  * @param  {Array} out | 
|  * @param  {number} arrDim | 
|  */ | 
|   | 
|   | 
| function catmullRomInterpolateArray(p0, p1, p2, p3, t, t2, t3, out, arrDim) { | 
|   var len = p0.length; | 
|   | 
|   if (arrDim === 1) { | 
|     for (var i = 0; i < len; i++) { | 
|       out[i] = catmullRomInterpolate(p0[i], p1[i], p2[i], p3[i], t, t2, t3); | 
|     } | 
|   } else { | 
|     var len2 = p0[0].length; | 
|   | 
|     for (var i = 0; i < len; i++) { | 
|       for (var j = 0; j < len2; j++) { | 
|         out[i][j] = catmullRomInterpolate(p0[i][j], p1[i][j], p2[i][j], p3[i][j], t, t2, t3); | 
|       } | 
|     } | 
|   } | 
| } | 
| /** | 
|  * Catmull Rom interpolate number | 
|  * @param  {number} p0 | 
|  * @param  {number} p1 | 
|  * @param  {number} p2 | 
|  * @param  {number} p3 | 
|  * @param  {number} t | 
|  * @param  {number} t2 | 
|  * @param  {number} t3 | 
|  * @return {number} | 
|  */ | 
|   | 
|   | 
| function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { | 
|   var v0 = (p2 - p0) * 0.5; | 
|   var v1 = (p3 - p1) * 0.5; | 
|   return (2 * (p1 - p2) + v0 + v1) * t3 + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + v0 * t + p1; | 
| } | 
| /** | 
|  * @param  {number} p0 | 
|  * @param  {number} p1 | 
|  * @param  {number} percent | 
|  * @return {number} | 
|  */ | 
|   | 
|   | 
| function interpolateNumber(p0, p1, percent) { | 
|   return (p1 - p0) * percent + p0; | 
| } | 
| /** | 
|  * @param  {string} p0 | 
|  * @param  {string} p1 | 
|  * @param  {number} percent | 
|  * @return {string} | 
|  */ | 
|   | 
|   | 
| function interpolateString(p0, p1, percent) { | 
|   return percent > 0.5 ? p1 : p0; | 
| } | 
| /** | 
|  * @param  {Array} p0 | 
|  * @param  {Array} p1 | 
|  * @param  {number} percent | 
|  * @param  {Array} out | 
|  * @param  {number} arrDim | 
|  */ | 
|   | 
|   | 
| function interpolateArray(p0, p1, percent, out, arrDim) { | 
|   var len = p0.length; | 
|   | 
|   if (arrDim === 1) { | 
|     for (var i = 0; i < len; i++) { | 
|       out[i] = interpolateNumber(p0[i], p1[i], percent); | 
|     } | 
|   } else { | 
|     var len2 = len && p0[0].length; | 
|   | 
|     for (var i = 0; i < len; i++) { | 
|       for (var j = 0; j < len2; j++) { | 
|         out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent); | 
|       } | 
|     } | 
|   } | 
| } | 
|   | 
| function rgba2String(rgba) { | 
|   rgba[0] = Math.floor(rgba[0]); | 
|   rgba[1] = Math.floor(rgba[1]); | 
|   rgba[2] = Math.floor(rgba[2]); | 
|   return 'rgba(' + rgba.join(',') + ')'; | 
| } | 
|   | 
| function cloneFrameValue(value) { | 
|   if (isArrayLike(value)) { | 
|     var len = value.length; | 
|   | 
|     if (isArrayLike(value[0])) { | 
|       var ret = []; | 
|   | 
|       for (var i = 0; i < len; i++) { | 
|         ret.push(arraySlice.call(value[i])); | 
|       } | 
|   | 
|       return ret; | 
|     } | 
|   | 
|     return arraySlice.call(value); | 
|   } | 
|   | 
|   return value; | 
| } | 
|   | 
| function defaultGetter(target, key) { | 
|   return target[key]; | 
| } | 
|   | 
| function defaultSetter(target, key, value) { | 
|   target[key] = value; | 
| } | 
|   | 
| exports.createTrackClip = createTrackClip; | 
| exports.cloneFrameValue = cloneFrameValue; | 
| exports.defaultGetter = defaultGetter; | 
| exports.defaultSetter = defaultSetter; |