Index: pkg/polymer/lib/elements/polymer-animation/polymer-animation.html |
diff --git a/pkg/polymer/lib/elements/polymer-animation/polymer-animation.html b/pkg/polymer/lib/elements/polymer-animation/polymer-animation.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9d8034388ade2a7a46bdc43e501f4d5fcd04058b |
--- /dev/null |
+++ b/pkg/polymer/lib/elements/polymer-animation/polymer-animation.html |
@@ -0,0 +1,301 @@ |
+<link rel="import" href="../polymer/polymer.html"> |
+<link rel="import" href="web-animations.html"> |
+<link rel="import" href="polymer-animation-keyframe.html"> |
+<link rel="import" href="polymer-animation-prop.html"> |
+ |
+<polymer-element name="polymer-animation" attributes="target keyframes sample composite duration fill easing iterationStart iterationCount delay direction autoplay targetSelector"> |
+ <script> |
+ (function() { |
+ function findTarget(inSelector, inNode) { |
+ var p = inNode; |
+ var target; |
+ while (p && !p.host && p !== document) { |
+ p = p.parentNode; |
+ } |
+ if (p) { |
+ target = p.querySelector(inSelector); |
+ } |
+ if (!target && p && p.host) { |
+ target = findTarget(inSelector, p.host); |
+ } |
+ return target; |
+ }; |
+ function toNumber(value, allowInfinity) { |
+ return (allowInfinity && value === 'Infinity') ? Number.POSITIVE_INFINITY : Number(value); |
+ }; |
+ /** |
+ * WebAnimations module. |
+ * @module Animation |
+ * @main animation |
+ * @example toolkitchen/labs/animation2/grid-fade.html |
+ * @example toolkitchen/labs/animation2/group.html |
+ */ |
+ /** |
+ * Component for a single animation. |
+ * |
+ * A animation component to fade out an element: |
+ * |
+ * <polymer-animation id="fadeout"> |
+ * <polymer-animation-keyframe offset="0"> |
+ * <polymer-animation-prop name="opacity" value="0"> |
+ * </polymer-animation-prop> |
+ * </polymer-animation-keyframe> |
+ * <polymer-animation-keyframe offset="1"> |
+ * <polymer-animation-prop name="opacity" value="1"> |
+ * </polymer-animation-prop> |
+ * </polymer-animation-keyframe> |
+ * </polymer-animation> |
+ * @class polymer-animation |
+ */ |
+ /** |
+ * Fired when the animation starts |
+ * @event polymer-animation-start |
+ * |
+ * Fired when the animation completes |
+ * @event polymer-animation-end |
+ * |
+ * Fired when the web animation object changes. |
+ * @event polymer-animation-change |
+ * |
+ */ |
+ Polymer('polymer-animation', { |
+ /** |
+ * One or more nodes to animate. |
+ * @property target |
+ * @type HTMLElement|Node|Array<HTMLElement|Node> |
+ */ |
+ target: null, |
+ /** |
+ * Selector for the node being animated. |
+ * @property targetSelector |
+ * @type String |
+ */ |
+ targetSelector: '', |
+ // animation |
+ /** |
+ * Animation keyframes specified as an array of dictionaries of |
+ * <css properties>:<array of values> pairs. For example, |
+ * @property keyframes |
+ * @type Object |
+ */ |
+ keyframes: null, |
+ /** |
+ * A custom animation function. Either provide this or keyframes. |
+ * @property sample |
+ * @type Function |
+ */ |
+ sample: null, |
+ //accumulate: null, // not working in polyfill |
+ /** |
+ * The composition behavior. "replace", "add" or "merge". |
+ * @property composite |
+ * @type "replace"|"add"|"merge" |
+ * @default "replace" |
+ */ |
+ composite: 'replace', |
+ // timing |
+ /** |
+ * Animation duration in milliseconds, 'infinity' or 'auto'. 'auto' |
+ * means use the animation's intrinsic duration. |
+ * @property duration |
+ * @type Number|"Infinity"|"auto" |
+ * @default "auto" |
+ */ |
+ duration: 'auto', |
+ /** |
+ * "none", "forwards", "backwards", "both" or "auto". When set to |
+ * "auto", the fill is "none" for a polymer-animation and "both" |
+ * for a polymer-animation-group. |
+ * @property fill |
+ * @type "none"|"forwards"|"backwards"|"both"|"auto" |
+ * @default "auto" |
+ */ |
+ fill: 'auto', |
+ /** |
+ * A transition timing function. |
+ * @property easing |
+ * @type "linear"|"ease"|"ease-in"|"ease-out"|"ease-in-out"| |
+ * "step-start"|"step-middle"|"step-end" |
+ * @default "linear" |
+ */ |
+ easing: 'linear', |
+ /** |
+ * Number of iterations into the timed item in which to begin |
+ * the animation. e.g. 0.5 will cause the animation to begin |
+ * halfway through the first iteration. |
+ * @property iterationStart |
+ * @type Number |
+ * @default 0 |
+ */ |
+ iterationStart: 0, |
+ /** |
+ * @property iterationCount |
+ * @type Number|'Infinity' |
+ * @default 1 |
+ */ |
+ iterationCount: 1, |
+ /** |
+ * Delay in milliseconds. |
+ * @property delay |
+ * @type Number |
+ * @default 0 |
+ */ |
+ delay: 0, |
+ /** |
+ * @property direction |
+ * @type "normal"|"reverse"|"alternate"|"alternate-reverse" |
+ * @default "normal" |
+ */ |
+ direction: 'normal', |
+ /** |
+ * @property autoplay |
+ * @type Boolean |
+ * @default false |
+ */ |
+ autoplay: false, |
+ animation: false, |
+ observe: { |
+ target: 'apply', |
+ keyframes: 'apply', |
+ sample: 'apply', |
+ composite: 'apply', |
+ duration: 'apply', |
+ fill: 'apply', |
+ easing: 'apply', |
+ iterationCount: 'apply', |
+ delay: 'apply', |
+ direction: 'apply', |
+ autoplay: 'apply' |
+ }, |
+ ready: function() { |
+ this.apply(); |
+ }, |
+ /** |
+ * Plays the animation. |
+ * @method play |
+ */ |
+ play: function() { |
+ this.apply(); |
+ if (this.animation && !this.autoplay) { |
+ this.bindAnimationEvents(); |
+ this.player = document.timeline.play(this.animation); |
+ return this.player; |
+ } |
+ }, |
+ /** |
+ * Stops the animation. |
+ * @method cancel |
+ */ |
+ cancel: function() { |
+ if (this.player) { |
+ this.player.source = null; |
+ } |
+ }, |
+ hasTarget: function() { |
+ return this.target !== null; |
+ }, |
+ apply: function() { |
+ this.animation = null; |
+ this.animation = this.makeAnimation(); |
+ if (this.autoplay && this.animation) { |
+ this.play(); |
+ } |
+ return this.animation; |
+ }, |
+ makeSingleAnimation: function(target) { |
+ // XXX(yvonne): for selecting all the animated elements. |
+ target.classList.add('polymer-animation-target'); |
+ return new Animation(target, this.animationEffect, this.timingProps); |
+ }, |
+ makeAnimation: function() { |
+ // this.target && console.log('makeAnimation target', this.target, 'animation', this.animationEffect, 'timing', this.timingProps); |
+ if (!this.target) { |
+ return null; |
+ } |
+ var animation; |
+ if (Array.isArray(this.target)) { |
+ var array = []; |
+ this.target.forEach(function(t) { |
+ array.push(this.makeSingleAnimation(t)); |
+ }.bind(this)); |
+ animation = new ParGroup(array); |
+ } else { |
+ animation = this.makeSingleAnimation(this.target); |
+ } |
+ return animation; |
+ }, |
+ animationChanged: function() { |
+ // TODO: attributes are not case sensitive. |
+ // TODO: Sending 'this' with the event because if the children is |
+ // in ShadowDOM the sender becomes the shadow host. |
+ this.fire('polymer-animation-change', this); |
+ }, |
+ targetSelectorChanged: function() { |
+ if (this.targetSelector) { |
+ this.target = findTarget(this.targetSelector, this); |
+ } |
+ }, |
+ targetChanged: function(old) { |
+ if (old) { |
+ old.classList.remove('polymer-animation-target'); |
+ } |
+ }, |
+ get timingProps() { |
+ var props = {}; |
+ var timing = { |
+ fill: {}, |
+ easing: {}, |
+ delay: {isNumber: true}, |
+ iterationCount: {isNumber: true, allowInfinity: true}, |
+ direction: {}, |
+ duration: {isNumber: true} |
+ }; |
+ for (t in timing) { |
+ if (this[t] !== null) { |
+ var name = timing[t].property || t; |
+ props[name] = timing[t].isNumber && this[t] !== 'auto' ? |
+ toNumber(this[t], timing[t].allowInfinity) : this[t]; |
+ } |
+ } |
+ return props; |
+ }, |
+ get animationEffect() { |
+ var props = {}; |
+ var frames = []; |
+ var effect; |
+ if (this.keyframes) { |
+ frames = this.keyframes; |
+ } else if (!this.sample) { |
+ var children = this.querySelectorAll('polymer-animation-keyframe'); |
+ if (children.length === 0) { |
+ children = this.shadowRoot.querySelectorAll('polymer-animation-keyframe'); |
+ } |
+ Array.prototype.forEach.call(children, function(c) { |
+ frames.push(c.properties); |
+ }); |
+ } |
+ if (this.sample) { |
+ effect = {sample: this.sample}; |
+ } else { |
+ effect = new KeyframeEffect(frames, this.composite); |
+ } |
+ return effect; |
+ }, |
+ bindAnimationEvents: function() { |
+ if (!this.animation.onstart) { |
+ this.animation.onstart = this.animationStartHandler.bind(this); |
+ } |
+ if (!this.animation.onend) { |
+ this.animation.onend = this.animationEndHandler.bind(this); |
+ } |
+ }, |
+ animationStartHandler: function() { |
+ this.fire('polymer-animation-start'); |
+ }, |
+ animationEndHandler: function() { |
+ this.fire('polymer-animation-end'); |
+ } |
+ }); |
+ })(); |
+ </script> |
+</polymer-element> |