OLD | NEW |
(Empty) | |
| 1 var animationState = {}; |
| 2 animationState.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 : en
gine; |
| 16 } |
| 17 |
| 18 function 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 |
| 27 function 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.timeli
ne.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 + "_" + aIn
dex; |
| 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 a
rray 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.l
ength - 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, targetSp
ec.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 || 'p
arams' == 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) / ani
mation.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.form
ula)); |
| 193 // evaluate inline function to get value |
| 194 action.func = new Function('value', 'params', "return " + ac
tion.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 |
| 215 function 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 |
| 229 function init(engine, keyframe) { |
| 230 animationState.reset(engine); |
| 231 setupPaint(); |
| 232 setupBackend(animationState.displayEngine); |
| 233 keyframeInit(keyframe); |
| 234 } |
| 235 |
| 236 function 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 |
| 254 function 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 |
| 280 function 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 |
| 310 function setupPaint() { |
| 311 for (var prop in paints) { |
| 312 flattenPaint(paints[prop]); |
| 313 } |
| 314 } |
OLD | NEW |