Index: experimental/docs/animationCommon.js |
diff --git a/experimental/docs/animationCommon.js b/experimental/docs/animationCommon.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1733ec203200a68903eaa136f39a888b28d46ead |
--- /dev/null |
+++ b/experimental/docs/animationCommon.js |
@@ -0,0 +1,314 @@ |
+var animationState = {}; |
+animationState.reset = function (engine) { |
+ if ('string' === typeof engine) { |
+ this.defaultEngine = engine; |
+ } |
+ this.defaults = {}; |
+ this.displayList = []; |
+ this.displayDict = {}; |
+ this.start = null; |
+ this.time = 0; |
+ this.timeline = []; |
+ this.timelineIndex = 0; |
+ this.requestID = null; |
+ this.paused = false; |
+ this.displayEngine = 'undefined' === typeof engine ? this.defaultEngine : engine; |
+} |
+ |
+function addActions(frame, timeline) { |
+ var keyframe = keyframes[frame]; |
+ var len = keyframe.length; |
+ for (var i = 0; i < len; ++i) { |
+ var action = keyframe[i]; |
+ loopOver(action, timeline); |
+ } |
+} |
+ |
+function animateList(now) { |
+ if (animationState.paused) { |
+ return; |
+ } |
+ if (animationState.start == null) { |
+ animationState.start = now - animationState.time; |
+ } |
+ animationState.time = now - animationState.start; |
+ var stillAnimating = false; |
+ for (var index = animationState.timelineIndex; index < animationState.timeline.length; ++index) { |
+ var animation = animationState.timeline[index]; |
+ if (animation.time > animationState.time) { |
+ stillAnimating = true; |
+ break; |
+ } |
+ if (animation.time + animation.duration < animationState.time) { |
+ if (animation.finalized) { |
+ continue; |
+ } |
+ animation.finalized = true; |
+ } |
+ stillAnimating = true; |
+ var actions = animation.actions; |
+ for (var aIndex = 0; aIndex < actions.length; ++aIndex) { |
+ var action = actions[aIndex]; |
+ var hasDraw = 'draw' in action; |
+ var hasRef = 'ref' in action; |
+ var displayIndex; |
+ if (hasDraw) { |
+ var ref = hasRef ? action.ref : "anonymous_" + index + "_" + aIndex; |
+ assert('string' == typeof(ref)); |
+ if (ref in animationState.displayDict) { |
+ displayIndex = animationState.displayDict[ref]; |
+ } else { |
+ assert('string' == typeof(action.draw)); |
+ var draw = (new Function("return " + action.draw))(); |
+ assert('object' == typeof(draw)); |
+ var paint; |
+ if ('paint' in action) { |
+ assert('string' == typeof(action.paint)); |
+ paint = (new Function("return " + action.paint))(); |
+ assert('object' == typeof(paint) && !isArray(paint)); |
+ } else { |
+ paint = animationState.defaults.paint; |
+ } |
+ displayIndex = animationState.displayList.length; |
+ animationState.displayList.push( { "ref":ref, "draw":draw, "paint":paint, |
+ "drawSpec":action.draw, "paintSpec":action.paint, |
+ "drawCopied":false, "paintCopied":false, |
+ "drawDirty":true, "paintDirty":true, "once":false } ); |
+ animationState.displayDict[ref] = displayIndex; |
+ } |
+ } else if (hasRef) { |
+ assert('string' == typeof(action.ref)); |
+ displayIndex = animationState.displayDict[action.ref]; |
+ } else { |
+ assert(actions.length == 1); |
+ for (var prop in action) { |
+ if ('paint' == prop) { |
+ assert('string' == typeof(action[prop])); |
+ var obj = (new Function("return " + action[prop]))(); |
+ assert('object' == typeof(obj) && !isArray(obj)); |
+ animationState.defaults[prop] = obj; |
+ } else { |
+ animationState.defaults[prop] = action[prop]; |
+ } |
+ } |
+ continue; |
+ } |
+ var targetSpec = 'target' in action ? action.target : animationState.defaults.target; |
+ assert(targetSpec); |
+ assert('string' == typeof(targetSpec)); |
+ assert(displayIndex < animationState.displayList.length); |
+ var display = animationState.displayList[displayIndex]; |
+ var modDraw = targetSpec.startsWith('draw'); |
+ assert(modDraw || targetSpec.startsWith('paint')); |
+ var modType = modDraw ? "draw" : "paint"; |
+ var copied = modDraw ? display.drawCopied : action.paintCopied; |
+ if (!copied) { |
+ var copy; |
+ if (!modDraw || display.drawSpec.startsWith("text")) { |
+ copy = {}; |
+ var original = modDraw ? display.draw : display.paint; |
+ for (var p in original) { |
+ copy[p] = original[p]; |
+ } |
+ } else if (display.drawSpec.startsWith("paths")) { |
+ copy = []; |
+ for (var i = 0; i < display.draw.length; ++i) { |
+ var curves = display.draw[i]; |
+ var curve = Object.keys(curves)[0]; |
+ copy[i] = {}; |
+ copy[i][curve] = curves[curve].slice(0); // clone the array of curves |
+ } |
+ } else { |
+ assert(display.drawSpec.startsWith("pictures")); |
+ copy = []; |
+ for (var i = 0; i < display.draw.length; ++i) { |
+ var entry = display.draw[i]; |
+ copy[i] = { "draw":entry.draw, "paint":entry.paint }; |
+ } |
+ } |
+ display[modType] = copy; |
+ display[modType + "Copied"] = true; |
+ } |
+ var targetField, targetObject, fieldOffset; |
+ if (targetSpec.endsWith("]")) { |
+ fieldOffset = targetSpec.lastIndexOf("["); |
+ assert(fieldOffset >= 0); |
+ targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length - 1); |
+ var arrayIndex = +targetField; |
+ if (!isNaN(arrayIndex) && targetField.length > 0) { |
+ targetField = arrayIndex; |
+ } |
+ |
+ } else { |
+ fieldOffset = targetSpec.lastIndexOf("."); |
+ if (fieldOffset >= 0) { |
+ targetField = targetSpec.substring(fieldOffset + 1, targetSpec.length); |
+ } else { |
+ targetObject = display; |
+ targetField = targetSpec; |
+ } |
+ } |
+ if (fieldOffset >= 0) { |
+ var sub = targetSpec.substring(0, fieldOffset); |
+ targetObject = (new Function('display', "return display." + sub))(display); |
+ } |
+ assert(null != targetObject[targetField]); |
+ if (!('start' in action) || action.start < animation.time) { |
+ for (var p in animationState.defaults) { |
+ if ('draw' == p || 'paint' == p || 'ref' == p) { |
+ continue; |
+ } |
+ assert('range' == p || 'target' == p || 'formula' == p || 'params' == p); |
+ if (!(p in action)) { |
+ action[p] = animationState.defaults[p]; |
+ } |
+ } |
+ if ('number' == typeof(action.formula)) { |
+ targetObject[targetField] = action.formula; |
+ action.once = true; |
+ } |
+ action.start = animation.time; |
+ } |
+ if (action.once) { |
+ continue; |
+ } |
+ var value = Math.min(1, (animationState.time - animation.time) / animation.duration); |
+ var scaled = action.range[0] + (action.range[1] - action.range[0]) * value; |
+ if ('params' in action) { |
+ if (!('func' in action)) { |
+ if (isArray(action.params)) { |
+ action.funcParams = []; |
+ var len = action.params.length; |
+ for (var i = 0; i < len; ++i) { |
+ action.funcParams[i] = 'target' == action.params[i] |
+ ? targetObject[targetField] |
+ : (new Function("return " + action.params[i]))(); |
+ } |
+ } else { |
+ action.funcParams = 'target' == action.params |
+ ? targetObject[targetField] |
+ : (new Function("return " + action.params))(); |
+ } |
+ assert('formula' in action && 'string' == typeof(action.formula)); |
+ // evaluate inline function to get value |
+ action.func = new Function('value', 'params', "return " + action.formula); |
+ } |
+ scaled = action.func(scaled, action.funcParams); |
+ } |
+ if (targetObject[targetField] != scaled) { |
+ if (modDraw) { |
+ display.drawDirty = true; |
+ } else { |
+ display.paintDirty = true; |
+ } |
+ targetObject[targetField] = scaled; |
+ } |
+ } |
+ } |
+ displayBackend(animationState.displayEngine, animationState.displayList); |
+ |
+ if (stillAnimating) { |
+ animationState.requestID = requestAnimationFrame(animateList); |
+ } |
+} |
+ |
+function flattenPaint(paint) { |
+ if (!paint.paint) { |
+ return; |
+ } |
+ var parent = paints[paint.paint]; |
+ flattenPaint(parent); |
+ for (var prop in parent) { |
+ if (!(prop in paint)) { |
+ paint[prop] = parent[prop]; |
+ } |
+ } |
+ paint.paint = null; |
+} |
+ |
+function init(engine, keyframe) { |
+ animationState.reset(engine); |
+ setupPaint(); |
+ setupBackend(animationState.displayEngine); |
+ keyframeInit(keyframe); |
+} |
+ |
+function keyframeInit(frame) { |
+ animationState.reset(); |
+ addActions("_default", animationState.timeline); |
+ addActions(frame, animationState.timeline); |
+ for (var index = 0; index < animationState.timeline.length; ++index) { |
+ animationState.timeline[index].position = index; |
+ } |
+ animationState.timeline.sort(function(a, b) { |
+ if (a.time == b.time) { |
+ return a.position - b.position; |
+ } |
+ return a.time - b.time; |
+ }); |
+ keyframeBackendInit(animationState.displayEngine, animationState.displayList, |
+ keyframes[frame][0]); |
+ animationState.requestID = requestAnimationFrame(animateList); |
+} |
+ |
+function loopAddProp(action, propName) { |
+ var funcStr = ""; |
+ var prop = action[propName]; |
+ if ('draw' != propName && isArray(prop)) { |
+ funcStr += '['; |
+ for (var index = 0; index < prop.length; ++index) { |
+ funcStr += loopAddProp(prop, index); |
+ if (index + 1 < prop.length) { |
+ funcStr += ", "; |
+ } |
+ } |
+ funcStr += ']'; |
+ return funcStr; |
+ } |
+ assert("object" != typeof(prop)); |
+ var useString = "string" == typeof(prop) && isAlpha(prop.charCodeAt(0)); |
+ if (useString) { |
+ funcStr += "'"; |
+ } |
+ funcStr += prop; |
+ if (useString) { |
+ funcStr += "'"; |
+ } |
+ return funcStr; |
+} |
+ |
+function loopOver(rec, timeline) { |
+ var funcStr = ""; |
+ if (rec.for) { |
+ funcStr += "for (" + rec.for[0] + "; " + rec.for[1] + "; " + rec.for[2] + ") {\n"; |
+ } |
+ funcStr += " var time = " + ('time' in rec ? rec.time : 0) + ";\n"; |
+ funcStr += " var duration = " + ('duration' in rec ? rec.duration : 0) + ";\n"; |
+ funcStr += " var actions = [];\n"; |
+ var len = rec.actions.length; |
+ for (var i = 0; i < len; ++i) { |
+ funcStr += " var action" + i + " = {\n"; |
+ var action = rec.actions[i]; |
+ for (var p in action) { |
+ funcStr += " '" + p + "':"; |
+ funcStr += loopAddProp(action, p); |
+ funcStr += ",\n"; |
+ } |
+ funcStr = funcStr.substring(0, funcStr.length - 2); |
+ funcStr += "\n };\n"; |
+ funcStr += " actions.push(action" + i + ");\n"; |
+ } |
+ funcStr += " timeline.push( { 'time':time, 'duration':duration, 'actions':actions," |
+ + "'finalized':false } );\n"; |
+ if (rec.for) { |
+ funcStr += "}\n"; |
+ } |
+ var func = new Function('rec', 'timeline', funcStr); |
+ func(rec, timeline); |
+} |
+ |
+function setupPaint() { |
+ for (var prop in paints) { |
+ flattenPaint(paints[prop]); |
+ } |
+} |