OLD | NEW |
(Empty) | |
| 1 <link rel="import" href="../polymer/polymer.html"> |
| 2 <link rel="import" href="web-animations.html"> |
| 3 <link rel="import" href="polymer-animation-keyframe.html"> |
| 4 <link rel="import" href="polymer-animation-prop.html"> |
| 5 |
| 6 <polymer-element name="polymer-animation" attributes="target keyframes sample co
mposite duration fill easing iterationStart iterationCount delay direction autop
lay targetSelector"> |
| 7 <script> |
| 8 (function() { |
| 9 function findTarget(inSelector, inNode) { |
| 10 var p = inNode; |
| 11 var target; |
| 12 while (p && !p.host && p !== document) { |
| 13 p = p.parentNode; |
| 14 } |
| 15 if (p) { |
| 16 target = p.querySelector(inSelector); |
| 17 } |
| 18 if (!target && p && p.host) { |
| 19 target = findTarget(inSelector, p.host); |
| 20 } |
| 21 return target; |
| 22 }; |
| 23 function toNumber(value, allowInfinity) { |
| 24 return (allowInfinity && value === 'Infinity') ? Number.POSITIVE_INFINIT
Y : Number(value); |
| 25 }; |
| 26 /** |
| 27 * WebAnimations module. |
| 28 * @module Animation |
| 29 * @main animation |
| 30 * @example toolkitchen/labs/animation2/grid-fade.html |
| 31 * @example toolkitchen/labs/animation2/group.html |
| 32 */ |
| 33 /** |
| 34 * Component for a single animation. |
| 35 * |
| 36 * A animation component to fade out an element: |
| 37 * |
| 38 * <polymer-animation id="fadeout"> |
| 39 * <polymer-animation-keyframe offset="0"> |
| 40 * <polymer-animation-prop name="opacity" value="0"> |
| 41 * </polymer-animation-prop> |
| 42 * </polymer-animation-keyframe> |
| 43 * <polymer-animation-keyframe offset="1"> |
| 44 * <polymer-animation-prop name="opacity" value="1"> |
| 45 * </polymer-animation-prop> |
| 46 * </polymer-animation-keyframe> |
| 47 * </polymer-animation> |
| 48 * @class polymer-animation |
| 49 */ |
| 50 /** |
| 51 * Fired when the animation starts |
| 52 * @event polymer-animation-start |
| 53 * |
| 54 * Fired when the animation completes |
| 55 * @event polymer-animation-end |
| 56 * |
| 57 * Fired when the web animation object changes. |
| 58 * @event polymer-animation-change |
| 59 * |
| 60 */ |
| 61 Polymer('polymer-animation', { |
| 62 /** |
| 63 * One or more nodes to animate. |
| 64 * @property target |
| 65 * @type HTMLElement|Node|Array<HTMLElement|Node> |
| 66 */ |
| 67 target: null, |
| 68 /** |
| 69 * Selector for the node being animated. |
| 70 * @property targetSelector |
| 71 * @type String |
| 72 */ |
| 73 targetSelector: '', |
| 74 // animation |
| 75 /** |
| 76 * Animation keyframes specified as an array of dictionaries of |
| 77 * <css properties>:<array of values> pairs. For example, |
| 78 * @property keyframes |
| 79 * @type Object |
| 80 */ |
| 81 keyframes: null, |
| 82 /** |
| 83 * A custom animation function. Either provide this or keyframes. |
| 84 * @property sample |
| 85 * @type Function |
| 86 */ |
| 87 sample: null, |
| 88 //accumulate: null, // not working in polyfill |
| 89 /** |
| 90 * The composition behavior. "replace", "add" or "merge". |
| 91 * @property composite |
| 92 * @type "replace"|"add"|"merge" |
| 93 * @default "replace" |
| 94 */ |
| 95 composite: 'replace', |
| 96 // timing |
| 97 /** |
| 98 * Animation duration in milliseconds, 'infinity' or 'auto'. 'auto' |
| 99 * means use the animation's intrinsic duration. |
| 100 * @property duration |
| 101 * @type Number|"Infinity"|"auto" |
| 102 * @default "auto" |
| 103 */ |
| 104 duration: 'auto', |
| 105 /** |
| 106 * "none", "forwards", "backwards", "both" or "auto". When set to |
| 107 * "auto", the fill is "none" for a polymer-animation and "both" |
| 108 * for a polymer-animation-group. |
| 109 * @property fill |
| 110 * @type "none"|"forwards"|"backwards"|"both"|"auto" |
| 111 * @default "auto" |
| 112 */ |
| 113 fill: 'auto', |
| 114 /** |
| 115 * A transition timing function. |
| 116 * @property easing |
| 117 * @type "linear"|"ease"|"ease-in"|"ease-out"|"ease-in-out"| |
| 118 * "step-start"|"step-middle"|"step-end" |
| 119 * @default "linear" |
| 120 */ |
| 121 easing: 'linear', |
| 122 /** |
| 123 * Number of iterations into the timed item in which to begin |
| 124 * the animation. e.g. 0.5 will cause the animation to begin |
| 125 * halfway through the first iteration. |
| 126 * @property iterationStart |
| 127 * @type Number |
| 128 * @default 0 |
| 129 */ |
| 130 iterationStart: 0, |
| 131 /** |
| 132 * @property iterationCount |
| 133 * @type Number|'Infinity' |
| 134 * @default 1 |
| 135 */ |
| 136 iterationCount: 1, |
| 137 /** |
| 138 * Delay in milliseconds. |
| 139 * @property delay |
| 140 * @type Number |
| 141 * @default 0 |
| 142 */ |
| 143 delay: 0, |
| 144 /** |
| 145 * @property direction |
| 146 * @type "normal"|"reverse"|"alternate"|"alternate-reverse" |
| 147 * @default "normal" |
| 148 */ |
| 149 direction: 'normal', |
| 150 /** |
| 151 * @property autoplay |
| 152 * @type Boolean |
| 153 * @default false |
| 154 */ |
| 155 autoplay: false, |
| 156 animation: false, |
| 157 observe: { |
| 158 target: 'apply', |
| 159 keyframes: 'apply', |
| 160 sample: 'apply', |
| 161 composite: 'apply', |
| 162 duration: 'apply', |
| 163 fill: 'apply', |
| 164 easing: 'apply', |
| 165 iterationCount: 'apply', |
| 166 delay: 'apply', |
| 167 direction: 'apply', |
| 168 autoplay: 'apply' |
| 169 }, |
| 170 ready: function() { |
| 171 this.apply(); |
| 172 }, |
| 173 /** |
| 174 * Plays the animation. |
| 175 * @method play |
| 176 */ |
| 177 play: function() { |
| 178 this.apply(); |
| 179 if (this.animation && !this.autoplay) { |
| 180 this.bindAnimationEvents(); |
| 181 this.player = document.timeline.play(this.animation); |
| 182 return this.player; |
| 183 } |
| 184 }, |
| 185 /** |
| 186 * Stops the animation. |
| 187 * @method cancel |
| 188 */ |
| 189 cancel: function() { |
| 190 if (this.player) { |
| 191 this.player.source = null; |
| 192 } |
| 193 }, |
| 194 hasTarget: function() { |
| 195 return this.target !== null; |
| 196 }, |
| 197 apply: function() { |
| 198 this.animation = null; |
| 199 this.animation = this.makeAnimation(); |
| 200 if (this.autoplay && this.animation) { |
| 201 this.play(); |
| 202 } |
| 203 return this.animation; |
| 204 }, |
| 205 makeSingleAnimation: function(target) { |
| 206 // XXX(yvonne): for selecting all the animated elements. |
| 207 target.classList.add('polymer-animation-target'); |
| 208 return new Animation(target, this.animationEffect, this.timingProps); |
| 209 }, |
| 210 makeAnimation: function() { |
| 211 // this.target && console.log('makeAnimation target', this.target, 'an
imation', this.animationEffect, 'timing', this.timingProps); |
| 212 if (!this.target) { |
| 213 return null; |
| 214 } |
| 215 var animation; |
| 216 if (Array.isArray(this.target)) { |
| 217 var array = []; |
| 218 this.target.forEach(function(t) { |
| 219 array.push(this.makeSingleAnimation(t)); |
| 220 }.bind(this)); |
| 221 animation = new ParGroup(array); |
| 222 } else { |
| 223 animation = this.makeSingleAnimation(this.target); |
| 224 } |
| 225 return animation; |
| 226 }, |
| 227 animationChanged: function() { |
| 228 // TODO: attributes are not case sensitive. |
| 229 // TODO: Sending 'this' with the event because if the children is |
| 230 // in ShadowDOM the sender becomes the shadow host. |
| 231 this.fire('polymer-animation-change', this); |
| 232 }, |
| 233 targetSelectorChanged: function() { |
| 234 if (this.targetSelector) { |
| 235 this.target = findTarget(this.targetSelector, this); |
| 236 } |
| 237 }, |
| 238 targetChanged: function(old) { |
| 239 if (old) { |
| 240 old.classList.remove('polymer-animation-target'); |
| 241 } |
| 242 }, |
| 243 get timingProps() { |
| 244 var props = {}; |
| 245 var timing = { |
| 246 fill: {}, |
| 247 easing: {}, |
| 248 delay: {isNumber: true}, |
| 249 iterationCount: {isNumber: true, allowInfinity: true}, |
| 250 direction: {}, |
| 251 duration: {isNumber: true} |
| 252 }; |
| 253 for (t in timing) { |
| 254 if (this[t] !== null) { |
| 255 var name = timing[t].property || t; |
| 256 props[name] = timing[t].isNumber && this[t] !== 'auto' ? |
| 257 toNumber(this[t], timing[t].allowInfinity) : this[t]; |
| 258 } |
| 259 } |
| 260 return props; |
| 261 }, |
| 262 get animationEffect() { |
| 263 var props = {}; |
| 264 var frames = []; |
| 265 var effect; |
| 266 if (this.keyframes) { |
| 267 frames = this.keyframes; |
| 268 } else if (!this.sample) { |
| 269 var children = this.querySelectorAll('polymer-animation-keyframe'); |
| 270 if (children.length === 0) { |
| 271 children = this.shadowRoot.querySelectorAll('polymer-animation-key
frame'); |
| 272 } |
| 273 Array.prototype.forEach.call(children, function(c) { |
| 274 frames.push(c.properties); |
| 275 }); |
| 276 } |
| 277 if (this.sample) { |
| 278 effect = {sample: this.sample}; |
| 279 } else { |
| 280 effect = new KeyframeEffect(frames, this.composite); |
| 281 } |
| 282 return effect; |
| 283 }, |
| 284 bindAnimationEvents: function() { |
| 285 if (!this.animation.onstart) { |
| 286 this.animation.onstart = this.animationStartHandler.bind(this); |
| 287 } |
| 288 if (!this.animation.onend) { |
| 289 this.animation.onend = this.animationEndHandler.bind(this); |
| 290 } |
| 291 }, |
| 292 animationStartHandler: function() { |
| 293 this.fire('polymer-animation-start'); |
| 294 }, |
| 295 animationEndHandler: function() { |
| 296 this.fire('polymer-animation-end'); |
| 297 } |
| 298 }); |
| 299 })(); |
| 300 </script> |
| 301 </polymer-element> |
OLD | NEW |