1var animationState = {}; 2animationState.reset = function (engine) { 3 if ('string' === typeof engine) { 4 this.defaultEngine = engine; 5 } 6 this.defaults = {}; 7 this.displayList = []; 8 this.displayDict = {}; 9 this.start = null; 10 this.time = 0; 11 this.timeline = []; 12 this.timelineIndex = 0; 13 this.requestID = null; 14 this.paused = false; 15 this.displayEngine = 'undefined' === typeof engine ? this.defaultEngine : engine; 16} 17 18function addActions(frame, timeline) { 19 var keyframe = keyframes[frame]; 20 var len = keyframe.length; 21 for (var i = 0; i < len; ++i) { 22 var action = keyframe[i]; 23 loopOver(action, timeline); 24 } 25} 26 27function animateList(now) { 28 if (animationState.paused) { 29 return; 30 } 31 if (animationState.start == null) { 32 animationState.start = now - animationState.time; 33 } 34 animationState.time = now - animationState.start; 35 var stillAnimating = false; 36 for (var index = animationState.timelineIndex; index < animationState.timeline.length; ++index) { 37 var animation = animationState.timeline[index]; 38 if (animation.time > animationState.time) { 39 stillAnimating = true; 40 break; 41 } 42 if (animation.time + animation.duration < animationState.time) { 43 if (animation.finalized) { 44 continue; 45 } 46 animation.finalized = true; 47 } 48 stillAnimating = true; 49 var actions = animation.actions; 50 for (var aIndex = 0; aIndex < actions.length; ++aIndex) { 51 var action = actions[aIndex]; 52 var hasDraw = 'draw' in action; 53 var hasRef = 'ref' in action; 54 var displayIndex; 55 if (hasDraw) { 56 var ref = hasRef ? action.ref : "anonymous_" + index + "_" + aIndex; 57 assert('string' == typeof(ref)); 58 if (ref in animationState.displayDict) { 59 displayIndex = animationState.displayDict[ref]; 60 } else { 61 assert('string' == typeof(action.draw)); 62 var draw = (new Function("return " + action.draw))(); 63 assert('object' == typeof(draw)); 64 var paint; 65 if ('paint' in action) { 66 assert('string' == typeof(action.paint)); 67 paint = (new Function("return " + action.paint))(); 68 assert('object' == typeof(paint) && !isArray(paint)); 69 } else { 70 paint = animationState.defaults.paint; 71 } 72 displayIndex = animationState.displayList.length; 73 animationState.displayList.push( { "ref":ref, "draw":draw, "paint":paint, 74 "drawSpec":action.draw, "paintSpec":action.paint, 75 "drawCopied":false, "paintCopied":false, 76 "drawDirty":true, "paintDirty":true, "once":false } ); 77 animationState.displayDict[ref] = displayIndex; 78 } 79 } else if (hasRef) { 80 assert('string' == typeof(action.ref)); 81 displayIndex = animationState.displayDict[action.ref]; 82 } else { 83 assert(actions.length == 1); 84 for (var prop in action) { 85 if ('paint' == prop) { 86 assert('string' == typeof(action[prop])); 87 var obj = (new Function("return " + action[prop]))(); 88 assert('object' == typeof(obj) && !isArray(obj)); 89 animationState.defaults[prop] = obj; 90 } else { 91 animationState.defaults[prop] = action[prop]; 92 } 93 } 94 continue; 95 } 96 var targetSpec = 'target' in action ? action.target : animationState.defaults.target; 97 assert(targetSpec); 98 assert('string' == typeof(targetSpec)); 99 assert(displayIndex < animationState.displayList.length); 100 var display = animationState.displayList[displayIndex]; 101 var modDraw = targetSpec.startsWith('draw'); 102 assert(modDraw || targetSpec.startsWith('paint')); 103 var modType = modDraw ? "draw" : "paint"; 104 var copied = modDraw ? display.drawCopied : action.paintCopied; 105 if (!copied) { 106 var copy; 107 if (!modDraw || display.drawSpec.startsWith("text")) { 108 copy = {}; 109 var original = modDraw ? display.draw : display.paint; 110 for (var p in original) { 111 copy[p] = original[p]; 112 } 113 } else if (display.drawSpec.startsWith("paths")) { 114 copy = []; 115 for (var i = 0; i < display.draw.length; ++i) { 116 var curves = display.draw[i]; 117 var curve = Object.keys(curves)[0]; 118 copy[i] = {}; 119 copy[i][curve] = curves[curve].slice(0); // clone the array of curves 120 } 121 } else { 122 assert(display.drawSpec.startsWith("pictures")); 123 copy = []; 124 for (var i = 0; i < display.draw.length; ++i) { 125 var entry = display.draw[i]; 126 copy[i] = { "draw":entry.draw, "paint":entry.paint }; 127 } 128 } 129 display[modType] = copy; 130 display[modType + "Copied"] = true; 131 } 132 var targetField, targetObject, fieldOffset; 133 if (targetSpec.endsWith("]")) { 134 fieldOffset = targetSpec.lastIndexOf("["); 135 assert(fieldOffset >= 0); 136 targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length - 1); 137 var arrayIndex = +targetField; 138 if (!isNaN(arrayIndex) && targetField.length > 0) { 139 targetField = arrayIndex; 140 } 141 142 } else { 143 fieldOffset = targetSpec.lastIndexOf("."); 144 if (fieldOffset >= 0) { 145 targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length); 146 } else { 147 targetObject = display; 148 targetField = targetSpec; 149 } 150 } 151 if (fieldOffset >= 0) { 152 var sub = targetSpec.substring(0, fieldOffset); 153 targetObject = (new Function('display', "return display." + sub))(display); 154 } 155 assert(null != targetObject[targetField]); 156 if (!('start' in action) || action.start < animation.time) { 157 for (var p in animationState.defaults) { 158 if ('draw' == p || 'paint' == p || 'ref' == p) { 159 continue; 160 } 161 assert('range' == p || 'target' == p || 'formula' == p || 'params' == p); 162 if (!(p in action)) { 163 action[p] = animationState.defaults[p]; 164 } 165 } 166 if ('number' == typeof(action.formula)) { 167 targetObject[targetField] = action.formula; 168 action.once = true; 169 } 170 action.start = animation.time; 171 } 172 if (action.once) { 173 continue; 174 } 175 var value = Math.min(1, (animationState.time - animation.time) / animation.duration); 176 var scaled = action.range[0] + (action.range[1] - action.range[0]) * value; 177 if ('params' in action) { 178 if (!('func' in action)) { 179 if (isArray(action.params)) { 180 action.funcParams = []; 181 var len = action.params.length; 182 for (var i = 0; i < len; ++i) { 183 action.funcParams[i] = 'target' == action.params[i] 184 ? targetObject[targetField] 185 : (new Function("return " + action.params[i]))(); 186 } 187 } else { 188 action.funcParams = 'target' == action.params 189 ? targetObject[targetField] 190 : (new Function("return " + action.params))(); 191 } 192 assert('formula' in action && 'string' == typeof(action.formula)); 193 // evaluate inline function to get value 194 action.func = new Function('value', 'params', "return " + action.formula); 195 } 196 scaled = action.func(scaled, action.funcParams); 197 } 198 if (targetObject[targetField] != scaled) { 199 if (modDraw) { 200 display.drawDirty = true; 201 } else { 202 display.paintDirty = true; 203 } 204 targetObject[targetField] = scaled; 205 } 206 } 207 } 208 displayBackend(animationState.displayEngine, animationState.displayList); 209 210 if (stillAnimating) { 211 animationState.requestID = requestAnimationFrame(animateList); 212 } 213} 214 215function flattenPaint(paint) { 216 if (!paint.paint) { 217 return; 218 } 219 var parent = paints[paint.paint]; 220 flattenPaint(parent); 221 for (var prop in parent) { 222 if (!(prop in paint)) { 223 paint[prop] = parent[prop]; 224 } 225 } 226 paint.paint = null; 227} 228 229function init(engine, keyframe) { 230 animationState.reset(engine); 231 setupPaint(); 232 setupBackend(animationState.displayEngine); 233 keyframeInit(keyframe); 234} 235 236function keyframeInit(frame) { 237 animationState.reset(); 238 addActions("_default", animationState.timeline); 239 addActions(frame, animationState.timeline); 240 for (var index = 0; index < animationState.timeline.length; ++index) { 241 animationState.timeline[index].position = index; 242 } 243 animationState.timeline.sort(function(a, b) { 244 if (a.time == b.time) { 245 return a.position - b.position; 246 } 247 return a.time - b.time; 248 }); 249 keyframeBackendInit(animationState.displayEngine, animationState.displayList, 250 keyframes[frame][0]); 251 animationState.requestID = requestAnimationFrame(animateList); 252} 253 254function loopAddProp(action, propName) { 255 var funcStr = ""; 256 var prop = action[propName]; 257 if ('draw' != propName && isArray(prop)) { 258 funcStr += '['; 259 for (var index = 0; index < prop.length; ++index) { 260 funcStr += loopAddProp(prop, index); 261 if (index + 1 < prop.length) { 262 funcStr += ", "; 263 } 264 } 265 funcStr += ']'; 266 return funcStr; 267 } 268 assert("object" != typeof(prop)); 269 var useString = "string" == typeof(prop) && isAlpha(prop.charCodeAt(0)); 270 if (useString) { 271 funcStr += "'"; 272 } 273 funcStr += prop; 274 if (useString) { 275 funcStr += "'"; 276 } 277 return funcStr; 278} 279 280function loopOver(rec, timeline) { 281 var funcStr = ""; 282 if (rec.for) { 283 funcStr += "for (" + rec.for[0] + "; " + rec.for[1] + "; " + rec.for[2] + ") {\n"; 284 } 285 funcStr += " var time = " + ('time' in rec ? rec.time : 0) + ";\n"; 286 funcStr += " var duration = " + ('duration' in rec ? rec.duration : 0) + ";\n"; 287 funcStr += " var actions = [];\n"; 288 var len = rec.actions.length; 289 for (var i = 0; i < len; ++i) { 290 funcStr += " var action" + i + " = {\n"; 291 var action = rec.actions[i]; 292 for (var p in action) { 293 funcStr += " '" + p + "':"; 294 funcStr += loopAddProp(action, p); 295 funcStr += ",\n"; 296 } 297 funcStr = funcStr.substring(0, funcStr.length - 2); 298 funcStr += "\n };\n"; 299 funcStr += " actions.push(action" + i + ");\n"; 300 } 301 funcStr += " timeline.push( { 'time':time, 'duration':duration, 'actions':actions," 302 + "'finalized':false } );\n"; 303 if (rec.for) { 304 funcStr += "}\n"; 305 } 306 var func = new Function('rec', 'timeline', funcStr); 307 func(rec, timeline); 308} 309 310function setupPaint() { 311 for (var prop in paints) { 312 flattenPaint(paints[prop]); 313 } 314} 315