2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10 <link href=
"core-transition-pages.html" rel=
"import">
12 <core-style id=
"hero-transition">
13 /* Hide heroes that are not currently transitioning */
14 polyfill-next-selector { content: ':host
> [animate]:not(.core-selected) [hero]'; }
15 ::content
> [animate]:not(.core-selected) /deep/ [hero] {
19 polyfill-next-selector { content: ':host
> .core-selected[animate] [hero]'; }
20 ::content
> .core-selected[animate] /deep/ [hero] {
25 polyfill-next-selector { content: ':host
> * [hero-p]'; }
26 ::content
> * /deep/ [hero-p] {
27 -webkit-transition: box-shadow
100ms ease-out;
28 transition: box-shadow
100ms ease-out;
31 polyfill-next-selector { content: ':host
> [animate] [hero-p]'; }
32 ::content
> [animate] /deep/ [hero-p] {
33 box-shadow: none !important;
40 `hero-transition` transforms two elements in different pages such that they appear
41 to be shared across the pages.
45 <core-animated-pages transition="hero-transition">
46 <section layout horizontal>
47 <div id="div1" flex></div>
48 <div id="div2" flex hero-id="shared" hero></div>
51 <section layout horizontal>
52 <div id="div3" flex hero-id="shared" hero></div>
53 <div id="div4" flex></div>
56 </core-animated-pages>
58 In the above example, the elements `#div2` and `#div3` shares the same `hero-id`
59 attribute and a single element appears to translate and scale smoothly between
60 the two positions during a page transition.
62 Both elements from the source and destination pages must share the same `hero-id`
63 and must both contain the `hero` attribute to trigger the transition. The separate
64 `hero` attribute allows you to use binding to configure the transition:
68 <core-animated-pages transition="hero-transition">
69 <section layout horizontal>
70 <div id="div1" flex hero-id="shared" hero?="{{selected == 0}}"></div>
71 <div id="div2" flex hero-id="shared" hero?="{{selected == 1}}"></div>
74 <section layout horizontal>
75 <div id="div3" flex hero-id="shared" hero></div>
78 </core-animated-pages>
80 In the above example, either `#div1` or `#div2` scales to `#div3` during a page transition,
81 depending on the value of `selected`.
83 Because it is common to share elements with different `border-radius` values, by default
84 this transition will also animate the `border-radius` property.
86 You can configure the duration of the hero transition with the global variable
87 `CoreStyle.g.transitions.heroDuration`.
89 @class hero-transition
90 @extends core-transition-pages
94 <polymer-element name=
"hero-transition" extends=
"core-transition-pages">
98 var webkitStyles
= '-webkit-transition' in document
.documentElement
.style
99 var TRANSITION_CSSNAME
= webkitStyles
? '-webkit-transition' : 'transition';
100 var TRANSFORM_CSSNAME
= webkitStyles
? '-webkit-transform' : 'transform';
101 var TRANSITION_NAME
= webkitStyles
? 'webkitTransition' : 'transition';
102 var TRANSFORM_NAME
= webkitStyles
? 'webkitTransform' : 'transform';
104 var hasShadowDOMPolyfill
= window
.ShadowDOMPolyfill
;
108 go: function(scope
, options
) {
116 var duration
= options
&& options
.duration
||
117 (CoreStyle
.g
.transitions
.heroDuration
||
118 CoreStyle
.g
.transitions
.duration
);
120 scope
._heroes
.forEach(function(h
) {
121 var d
= h
.h0
.hasAttribute('hero-delayed') ? CoreStyle
.g
.transitions
.heroDelay
: '';
123 props
.forEach(function(p
) {
124 wt
.push(p
+ ' ' + duration
+ ' ' + options
.easing
+ ' ' + d
);
127 h
.h1
.style
[TRANSITION_NAME
] = wt
.join(', ');
128 h
.h1
.style
.borderRadius
= h
.r1
;
129 h
.h1
.style
[TRANSFORM_NAME
] = '';
132 this.super(arguments
);
134 if (!scope
._heroes
.length
) {
135 this.completed
= true;
139 prepare: function(scope
, options
) {
140 this.super(arguments
);
141 var src
= options
.src
, dst
= options
.dst
;
143 if (scope
._heroes
&& scope
._heroes
.length
) {
144 this.ensureComplete(scope
);
149 // FIXME(yvonne): basic support for nested pages.
150 // Look for heroes in the light DOM and one level of shadow DOM of the src and dst,
151 // and also in src.selectedItem or dst.selectedItem, then transform the dst hero to src
153 var h
$ = this.findAllInShadows(src
, ss
);
154 if (src
.selectedItem
) {
155 hs
$ = this.findAllInShadows(src
.selectedItem
, ss
);
157 // De-duplicate items
158 Array
.prototype.forEach
.call(hs
$, function(hs
) {
159 if (h
$.indexOf(hs
) === -1) {
163 h
$ = h
$.concat(hsa
$);
166 for (var i
=0, h0
; h0
=h
$[i
]; i
++) {
167 var v
= h0
.getAttribute('hero-id');
168 var ds
= '[hero][hero-id="' + v
+ '"]';
169 var h1
= this.findInShadows(dst
, ds
);
171 if (!h1
&& dst
.selectedItem
) {
172 h1
= this.findInShadows(dst
.selectedItem
, ds
);
175 // console.log('src', src);
176 // console.log('dst', dst, dst.selectedItem);
177 // console.log(v, h0, h1);
179 var c0
= getComputedStyle(h0
);
180 var c1
= getComputedStyle(h1
);
183 b0
: h0
.getBoundingClientRect(),
186 b1
: h1
.getBoundingClientRect(),
190 var dl
= h
.b0
.left
- h
.b1
.left
;
191 var dt
= h
.b0
.top
- h
.b1
.top
;
192 var sw
= h
.b0
.width
/ h
.b1
.width
;
193 var sh
= h
.b0
.height
/ h
.b1
.height
;
195 // h.scaley = h.h0.hasAttribute('scaley');
196 // if (!h.scaley && (sw !== 1 || sh !== 1)) {
198 // h.h1.style.width = h.b0.width + 'px';
199 // h.h1.style.height = h.b0.height + 'px';
202 // Also animate the border-radius for the circle-to-square transition
204 h
.h1
.style
.borderRadius
= h
.r0
;
209 h
.h1
.style
[TRANSFORM_NAME
] = 'translate(' + dl
+ 'px,' + dt
+ 'px)' + ' scale(' + sw
+ ',' + sh
+ ')';
210 h
.h1
.style
[TRANSFORM_NAME
+ 'Origin'] = '0 0';
212 scope
._heroes
.push(h
);
218 // carefully look into ::shadow with polyfill specific hack
219 findInShadows: function(node
, selector
) {
220 return node
.querySelector(selector
) || (hasShadowDOMPolyfill
?
221 queryAllShadows(node
, selector
) :
222 node
.querySelector('::shadow ' + selector
));
225 findAllInShadows: function(node
, selector
) {
226 if (hasShadowDOMPolyfill
) {
227 var nodes
= node
.querySelectorAll(selector
).array();
228 var shadowNodes
= queryAllShadows(node
, selector
, true);
229 return nodes
.concat(shadowNodes
);
231 return node
.querySelectorAll(selector
).array().concat(node
.shadowRoot
? node
.shadowRoot
.querySelectorAll(selector
).array() : []);
235 ensureComplete: function(scope
) {
236 this.super(arguments
);
238 scope
._heroes
.forEach(function(h
) {
239 h
.h1
.style
[TRANSITION_NAME
] = '';
240 h
.h1
.style
[TRANSFORM_NAME
] = '';
246 complete: function(scope
, e
) {
247 // if (e.propertyName === TRANSFORM_CSSNAME) {
249 scope
._heroes
.forEach(function(h
) {
250 if (h
.h1
=== e
.path
[0]) {
256 this.super(arguments
);
264 // utility method for searching through shadowRoots.
265 function queryShadow(node
, selector
) {
266 var m
, el
= node
.firstElementChild
;
269 sr
= node
.shadowRoot
;
272 sr
= sr
.olderShadowRoot
;
274 for(i
= shadows
.length
- 1; i
>= 0; i
--) {
275 m
= shadows
[i
].querySelector(selector
);
281 m
= queryShadow(el
, selector
);
285 el
= el
.nextElementSibling
;
290 function _queryAllShadows(node
, selector
, results
) {
291 var el
= node
.firstElementChild
;
292 var temp
, sr
, shadows
, i
, j
;
294 sr
= node
.shadowRoot
;
297 sr
= sr
.olderShadowRoot
;
299 for (i
= shadows
.length
- 1; i
>= 0; i
--) {
300 temp
= shadows
[i
].querySelectorAll(selector
);
301 for(j
= 0; j
< temp
.length
; j
++) {
302 results
.push(temp
[j
]);
306 _queryAllShadows(el
, selector
, results
);
307 el
= el
.nextElementSibling
;
312 queryAllShadows = function(node
, selector
, all
) {
314 return _queryAllShadows(node
, selector
, []);
316 return queryShadow(node
, selector
);
324 <hero-transition id=
"hero-transition"></hero-transition>