OLD | NEW |
(Empty) | |
| 1 <link href="../core-selector/core-selector.html" rel="import"> |
| 2 |
| 3 <link href="transitions/hero-transition.html" rel="import"> |
| 4 <link href="transitions/cross-fade.html" rel="import"> |
| 5 |
| 6 <!-- |
| 7 |
| 8 `core-animated-pages` selects one of its children "pages" to show and runs a tra
nsition |
| 9 when switching between them. The transitions are designed to be pluggable, and c
an |
| 10 accept any object that is an instance of a `core-transition-pages`. Transitions
to run |
| 11 are specified in the `transitions` attribute as a space-delimited string of `id`
s of |
| 12 transition elements. Several transitions are available with `core-animated-pages
` by |
| 13 default, including `hero-transition`, `cross-fade`, and `tile-cascade`. |
| 14 |
| 15 Example: |
| 16 |
| 17 <style> |
| 18 #hero1 { |
| 19 position: absolute; |
| 20 top: 0; |
| 21 left: 0; |
| 22 width: 300px; |
| 23 height: 300px; |
| 24 background-color: orange; |
| 25 } |
| 26 #hero2 { |
| 27 position: absolute; |
| 28 top: 200px; |
| 29 left: 300px; |
| 30 width: 300px; |
| 31 height: 300px; |
| 32 background-color: orange; |
| 33 } |
| 34 #bottom1, #bottom2 { |
| 35 position: absolute; |
| 36 bottom: 0; |
| 37 top: 0; |
| 38 left: 0; |
| 39 height: 50px; |
| 40 } |
| 41 #bottom1 { |
| 42 background-color: blue; |
| 43 } |
| 44 #bottom2 { |
| 45 background-color: green; |
| 46 } |
| 47 </style> |
| 48 // hero-transition and cross-fade are declared elsewhere |
| 49 <core-animated-pages transitions="hero-transition cross-fade"> |
| 50 <section id="page1"> |
| 51 <div id="hero1" hero-id="hero" hero></div> |
| 52 <div id="bottom1" cross-fade></div> |
| 53 </section> |
| 54 <section id="page2"> |
| 55 <div id="hero2" hero-id="hero" hero></div> |
| 56 <div id="bottom2" cross-fade></div> |
| 57 </section> |
| 58 </core-animated-pages> |
| 59 |
| 60 In the above example, two transitions (`hero-transition` and `cross-fade`) are r
un when switching |
| 61 between `page1` and `page2`. `hero-transition` transforms elements with the same
`hero-id` such |
| 62 that they appear to be shared across different pages. `cross-fade` fades out the
elements marked |
| 63 `cross-fade` in the outgoing page, and fades in those in the incoming page. See
the individual |
| 64 transition's documentation for specific details. |
| 65 |
| 66 Finding elements to transition |
| 67 ------------------------------ |
| 68 |
| 69 In general, a transition is applied to elements marked with a certain attribute.
For example, |
| 70 `hero-transition` applies the transition on elements with the `hero` and `hero-i
d` attribute. |
| 71 Among the transitions included with `core-animated-pages`, script-based transiti
ons such as |
| 72 `hero-transition` generally look for elements up to one level of shadowRoot from
the |
| 73 `core-animated-pages` element, and CSS-based transitions such as `cross-fade` lo
ok for elements |
| 74 within any shadowRoot within the `core-animated-pages` element. This means you c
an use |
| 75 custom elements as pages and mark elements in their shadowRoots as heroes, or ma
rk |
| 76 elements in deeper shadowRoots with other transitions. |
| 77 |
| 78 Example: |
| 79 |
| 80 <polymer-element name="x-el" noscript> |
| 81 <template> |
| 82 <style> |
| 83 #hero { |
| 84 position: absolute; |
| 85 top: 0; |
| 86 right: 0; |
| 87 width: 50px; |
| 88 height: 300px; |
| 89 background-color: blue; |
| 90 } |
| 91 </style> |
| 92 <div id="hero" hero-id="bar" hero></div> |
| 93 </template> |
| 94 </polymer-element> |
| 95 |
| 96 <polymer-element name="x-page-1" noscript> |
| 97 <template> |
| 98 <style> |
| 99 #hero1 { |
| 100 position: absolute; |
| 101 top: 0; |
| 102 left: 0; |
| 103 width: 300px; |
| 104 height: 300px; |
| 105 background-color: orange; |
| 106 } |
| 107 </style> |
| 108 <div id="hero1" hero-id="foo" hero></div> |
| 109 <div id="hero2" hero-id="bar" hero></div> |
| 110 </template> |
| 111 </polymer-element> |
| 112 |
| 113 <polymer-element name="x-page-2" noscript> |
| 114 <template> |
| 115 <style> |
| 116 #hero1 { |
| 117 position: absolute; |
| 118 top: 200px; |
| 119 left: 300px; |
| 120 width: 300px; |
| 121 height: 300px; |
| 122 background-color: orange; |
| 123 } |
| 124 #hero2 { |
| 125 background-color: blue; |
| 126 height: 150px; |
| 127 width: 400px; |
| 128 } |
| 129 </style> |
| 130 // The below element is one level of shadow from the core-animated-pages a
nd will |
| 131 // be transitioned. |
| 132 <div id="hero1" hero-id="foo" hero></div> |
| 133 // The below element contains a hero inside its shadowRoot making it two l
evels away |
| 134 // from the core-animated-pages, and will not be transitioned. |
| 135 <x-el></x-el> |
| 136 </template> |
| 137 </polymer-element> |
| 138 |
| 139 <core-animated-pages transitions="hero-transition"> |
| 140 <x-page-1></x-page-1> |
| 141 <x-page-2></x-page-2> |
| 142 </core-animated-pages> |
| 143 |
| 144 Note that the container element of the page does not participate in the transiti
on. |
| 145 |
| 146 // This does not work |
| 147 <core-animated-pages transitions="cross-fade"> |
| 148 <section cross-fade></section> |
| 149 <section cross-fade></section> |
| 150 </core-animated-pages> |
| 151 |
| 152 // This works |
| 153 <core-animated-pages transitions="cross-fade"> |
| 154 <section> |
| 155 <div cross-fade></div> |
| 156 </section> |
| 157 <section> |
| 158 <div cross-fade></div> |
| 159 </section> |
| 160 </core-animated-pages> |
| 161 |
| 162 Dynamically setting up transitions |
| 163 ---------------------------------- |
| 164 |
| 165 An easy way to dynamically set up transitions dynamically is to use property bin
ding on |
| 166 the transition attributes. |
| 167 |
| 168 Example: |
| 169 |
| 170 <core-animated-pages selected="{{selected}}"> |
| 171 <section id="page1"> |
| 172 <div hero-id="hero" hero></div> |
| 173 </section> |
| 174 <section id="page2"> |
| 175 <div id="foo" hero-id="hero" hero?="{{selected === 1 || selected === 0}}
" cross-fade="{{selected === 2}}"></div> |
| 176 </section> |
| 177 <section id="page3"> |
| 178 </section> |
| 179 </core-animated-pages> |
| 180 |
| 181 In the above example, the "foo" element only behaves as a hero element if transi
tioning between |
| 182 `#page1` and `#page2`. It gets cross-faded when transition to or from `#page3`. |
| 183 |
| 184 Nesting pages |
| 185 ------------- |
| 186 |
| 187 It is possible to nest core-animated-pages elements for organization. Excessive
nesting is |
| 188 not encouraged, however, since it makes setting up the transition more complex. |
| 189 |
| 190 To nest core-animated-pages, the page containing the nested core-animated-pages
element should |
| 191 have a `selectedItem` property bound to the `selectedItem` property of the neste
d element. This |
| 192 will allow the outer core-animated-pages to know which nested page it is actuall
y transitioning |
| 193 to. |
| 194 |
| 195 Example: |
| 196 |
| 197 <polymer-element name="nested-page" attributes="selectedItem"> |
| 198 <template> |
| 199 <core-animated-pages selectedItem="{{selectedItem}}"> |
| 200 ... |
| 201 </core-animated-pages> |
| 202 </template> |
| 203 </polymer-element> |
| 204 |
| 205 <core-animated-pages> |
| 206 <section id="page1"></section> |
| 207 <nested-page id="page2"></section> |
| 208 </core-animated-pages> |
| 209 |
| 210 @element core-animated-pages |
| 211 @extends core-selector |
| 212 @status beta |
| 213 @homepage github.io |
| 214 --> |
| 215 <!-- |
| 216 Fired before a page transition occurs. Both pages involved in the transition are
visible when |
| 217 this event fires. This is useful if there is something the client needs to do wh
en a page becomes |
| 218 visible. |
| 219 |
| 220 @event core-animated-pages-transition-prepare |
| 221 --> |
| 222 <!-- |
| 223 Fired when a page transition completes. |
| 224 |
| 225 @event core-animated-pages-transition-end |
| 226 --> |
| 227 <polymer-element name="core-animated-pages" extends="core-selector" notap attrib
utes="transitions"> |
| 228 |
| 229 <template> |
| 230 |
| 231 <link href="core-animated-pages.css" rel="stylesheet"> |
| 232 |
| 233 <shadow></shadow> |
| 234 |
| 235 </template> |
| 236 |
| 237 <script> |
| 238 |
| 239 Polymer({ |
| 240 |
| 241 eventDelegates: { |
| 242 'core-transitionend': 'transitionEnd' |
| 243 }, |
| 244 |
| 245 /** |
| 246 * A space-delimited string of transitions to use when switching between pag
es in this element. |
| 247 * The strings are `id`s of `core-transition-pages` elements included elsewh
ere. See the |
| 248 * individual transition's document for specific details. |
| 249 * |
| 250 * @attribute transitions |
| 251 * @type string |
| 252 * @default '' |
| 253 */ |
| 254 transitions: '', |
| 255 |
| 256 selected: 0, |
| 257 |
| 258 /** |
| 259 * The last page selected. This property is useful to dynamically set transi
tions based |
| 260 * on incoming and outgoing pages. |
| 261 * |
| 262 * @attribute lastSelected |
| 263 * @type Object |
| 264 * @default null |
| 265 */ |
| 266 lastSelected: null, |
| 267 |
| 268 registerCallback: function() { |
| 269 this.tmeta = document.createElement('core-transition'); |
| 270 }, |
| 271 |
| 272 created: function() { |
| 273 this._transitions = []; |
| 274 this.transitioning = []; |
| 275 }, |
| 276 |
| 277 transitionsChanged: function() { |
| 278 this._transitions = this.transitions.split(' '); |
| 279 }, |
| 280 |
| 281 _transitionsChanged: function(old) { |
| 282 if (this._transitionElements) { |
| 283 this._transitionElements.forEach(function(t) { |
| 284 t.teardown(this); |
| 285 }, this); |
| 286 } |
| 287 this._transitionElements = []; |
| 288 this._transitions.forEach(function(transitionId) { |
| 289 var t = this.getTransition(transitionId); |
| 290 if (t) { |
| 291 this._transitionElements.push(t); |
| 292 t.setup(this); |
| 293 } |
| 294 }, this); |
| 295 }, |
| 296 |
| 297 getTransition: function(transitionId) { |
| 298 return this.tmeta.byId(transitionId); |
| 299 }, |
| 300 |
| 301 selectionSelect: function(e, detail) { |
| 302 this.updateSelectedItem(); |
| 303 // Wait to call applySelection when we run the transition |
| 304 }, |
| 305 |
| 306 applyTransition: function(src, dst) { |
| 307 if (this.animating) { |
| 308 this.cancelAsync(this.animating); |
| 309 this.animating = null; |
| 310 } |
| 311 |
| 312 Platform.flush(); |
| 313 |
| 314 if (this.transitioning.indexOf(src) === -1) { |
| 315 this.transitioning.push(src); |
| 316 } |
| 317 if (this.transitioning.indexOf(dst) === -1) { |
| 318 this.transitioning.push(dst); |
| 319 } |
| 320 // force src, dst to display |
| 321 src.setAttribute('animate', ''); |
| 322 dst.setAttribute('animate', ''); |
| 323 // |
| 324 var options = { |
| 325 src: src, |
| 326 dst: dst, |
| 327 easing: 'cubic-bezier(0.4, 0, 0.2, 1)' |
| 328 } |
| 329 |
| 330 // fire an event so clients have a chance to do something when the |
| 331 // new page becomes visible but before it draws. |
| 332 this.fire('core-animated-pages-transition-prepare'); |
| 333 |
| 334 // |
| 335 // prepare transition |
| 336 this._transitionElements.forEach(function(transition) { |
| 337 transition.prepare(this, options); |
| 338 }, this); |
| 339 // |
| 340 // force layout! |
| 341 src.offsetTop; |
| 342 |
| 343 // |
| 344 // apply selection |
| 345 this.applySelection(dst, true); |
| 346 this.applySelection(src, false); |
| 347 // |
| 348 // start transition |
| 349 this._transitionElements.forEach(function(transition) { |
| 350 transition.go(this, options); |
| 351 }, this); |
| 352 |
| 353 if (!this._transitionElements.length) { |
| 354 this.complete(); |
| 355 } else { |
| 356 this.animating = this.async(this.complete.bind(this), null, 5000); |
| 357 } |
| 358 }, |
| 359 |
| 360 complete: function() { |
| 361 if (this.animating) { |
| 362 this.cancelAsync(this.animating); |
| 363 this.animating = null; |
| 364 } |
| 365 |
| 366 this.transitioning.forEach(function(t) { |
| 367 t.removeAttribute('animate'); |
| 368 }); |
| 369 this.transitioning = []; |
| 370 |
| 371 this._transitionElements.forEach(function(transition) { |
| 372 transition.ensureComplete(this); |
| 373 }, this); |
| 374 |
| 375 this.fire('core-animated-pages-transition-end'); |
| 376 }, |
| 377 |
| 378 transitionEnd: function(e) { |
| 379 if (this.transitioning.length) { |
| 380 var completed = true; |
| 381 this._transitionElements.forEach(function(transition) { |
| 382 if (!transition.completed) { |
| 383 completed = false; |
| 384 } |
| 385 }); |
| 386 if (completed) { |
| 387 this.job('transitionWatch', function() { |
| 388 this.complete(); |
| 389 }, 100); |
| 390 } |
| 391 } |
| 392 }, |
| 393 |
| 394 selectedChanged: function(old) { |
| 395 this.lastSelected = old; |
| 396 this.super(arguments); |
| 397 }, |
| 398 |
| 399 selectedItemChanged: function(oldItem) { |
| 400 this.super(arguments); |
| 401 |
| 402 if (!oldItem) { |
| 403 this.applySelection(this.selectedItem, true); |
| 404 return; |
| 405 } |
| 406 |
| 407 if (this.hasAttribute('no-transition') || !this._transitionElements || !th
is._transitionElements.length) { |
| 408 this.applySelection(oldItem, false); |
| 409 this.applySelection(this.selectedItem, true); |
| 410 return; |
| 411 } |
| 412 |
| 413 if (oldItem && this.selectedItem) { |
| 414 // TODO(sorvell): allow bindings to update first? |
| 415 var self = this; |
| 416 Platform.flush(); |
| 417 Platform.endOfMicrotask(function() { |
| 418 self.applyTransition(oldItem, self.selectedItem); |
| 419 }); |
| 420 } |
| 421 } |
| 422 |
| 423 }); |
| 424 |
| 425 </script> |
| 426 |
| 427 </polymer-element> |
OLD | NEW |