Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(160)

Side by Side Diff: chrome/browser/resources/settings/settings_page/main_page_behavior.js

Issue 2106013002: Move settings-section animations into setting-section, make better (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@Transitions
Patch Set: Computed properties Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // Fast out, slow in.
6 var EASING_FUNCTION = 'cubic-bezier(0.4, 0, 0.2, 1)';
7 var EXPAND_DURATION = 350;
8
9 /** 5 /**
10 * Calls |readyTest| repeatedly until it returns true, then calls 6 * Calls |readyTest| repeatedly until it returns true, then calls
11 * |readyCallback|. 7 * |readyCallback|.
12 * @param {function():boolean} readyTest 8 * @param {function():boolean} readyTest
13 * @param {!Function} readyCallback 9 * @param {!Function} readyCallback
14 */ 10 */
15 function doWhenReady(readyTest, readyCallback) { 11 function doWhenReady(readyTest, readyCallback) {
16 // TODO(dschuyler): Determine whether this hack can be removed. 12 // TODO(dschuyler): Determine whether this hack can be removed.
17 // See also: https://github.com/Polymer/polymer/issues/3629 13 // See also: https://github.com/Polymer/polymer/issues/3629
18 var intervalId = setInterval(function() { 14 var intervalId = setInterval(function() {
(...skipping 10 matching lines...) Expand all
29 * section should be expanded at any given time. 25 * section should be expanded at any given time.
30 * @polymerBehavior Polymer.MainPageBehavior 26 * @polymerBehavior Polymer.MainPageBehavior
31 */ 27 */
32 var MainPageBehaviorImpl = { 28 var MainPageBehaviorImpl = {
33 /** 29 /**
34 * @type {string} Selector to get the sections. Derived elements 30 * @type {string} Selector to get the sections. Derived elements
35 * must override. 31 * must override.
36 */ 32 */
37 sectionSelector: '', 33 sectionSelector: '',
38 34
39 /** @type {?Element} The scrolling container. */
40 scroller: null,
41
42 /** @override */ 35 /** @override */
43 attached: function() { 36 attached: function() {
37 /** @type {!HTMLElement} The scrolling container. */
44 this.scroller = this.domHost && this.domHost.parentNode.$.mainContainer; 38 this.scroller = this.domHost && this.domHost.parentNode.$.mainContainer;
45 }, 39 },
46 40
47 /** 41 /**
48 * Hides or unhides the sections not being expanded. 42 * Hides or unhides the sections not being expanded.
49 * @param {string} sectionName The section to keep visible. 43 * @param {string} sectionName The section to keep visible.
50 * @param {boolean} hidden Whether the sections should be hidden. 44 * @param {boolean} hidden Whether the sections should be hidden.
51 * @private 45 * @private
52 */ 46 */
53 toggleOtherSectionsHidden_: function(sectionName, hidden) { 47 toggleOtherSectionsHidden_: function(sectionName, hidden) {
54 var sections = Polymer.dom(this.root).querySelectorAll( 48 var sections = Polymer.dom(this.root).querySelectorAll(
55 this.sectionSelector + ':not([section=' + sectionName + '])'); 49 this.sectionSelector + ':not([section=' + sectionName + '])');
56 for (var section of sections) 50 for (var section of sections)
57 section.hidden = hidden; 51 section.hidden = hidden;
58 }, 52 },
59 53
60 /** 54 /**
61 * Animates the card in |section|, expanding it to fill the page. 55 * Animates the card in |section|, expanding it to fill the page.
62 * @param {!SettingsSectionElement} section 56 * @param {!SettingsSectionElement} section
63 */ 57 */
64 expandSection: function(section) { 58 expandSection: function(section) {
65 // If another section's card is expanding, cancel that animation first. 59 // TODO(michaelpg): Manage transition lifetime better.
66 var expanding = this.$$('.expanding'); 60 // crbug.com/624145
67 if (expanding) { 61 if (this.openSectionTransition_) {
68 if (expanding == section) 62 if (this.openSectionTransition_.section == section)
69 return; 63 return;
70 64
71 if (this.animations['section']) { 65 // Cancel the other section expanding.
72 // Cancel the animation, then call startExpandSection_. 66 this.openSectionTransition_.cancel();
73 this.cancelAnimation('section', function() { 67 // After everything, schedule this function again to open the section.
74 this.startExpandSection_(section); 68 this.openSectionTransition_.finished
75 }.bind(this)); 69 .then(function() {
76 } else { 70 // The animation already finished, so close that section.
77 // The animation must have finished but its promise hasn't resolved yet. 71 this.collapseSection(this.openSectionTransition_.section);
78 // When it resolves, collapse that section's card before expanding 72 }).catch(function() {})
79 // this one. 73 .then(this.expandSection.bind(this, section));
80 setTimeout(function() { 74 return;
81 this.collapseSection( 75 }
82 /** @type {!SettingsSectionElement} */(expanding)); 76 if (this.closeSectionTransition_) {
83 this.finishAnimation('section', function() { 77 // Finish collapsing the section, then expand this section.
84 this.startExpandSection_(section); 78 this.closeSectionTransition_.finished
85 }.bind(this)); 79 .then(this.expandSection.bind(this, section));
Dan Beam 2016/07/12 02:01:54 i don't see lines like this as a big improvement w
michaelpg 2016/07/20 22:10:57 Acknowledged. Breaking here after .then( instead.
86 }.bind(this));
87 }
88
89 return; 80 return;
90 } 81 }
91 82
92 if (this.$$('.collapsing') && this.animations['section']) { 83 this.openSectionTransition_ =
93 // Finish the collapse animation before expanding. 84 new settings.OpenSectionTransition(section, this.scroller);
94 this.finishAnimation('section', function() {
95 this.startExpandSection_(section);
96 }.bind(this));
97 return;
98 }
99
100 this.startExpandSection_(section);
101 },
102
103 /**
104 * Helper function to set up and start the expand animation.
105 * @param {!SettingsSectionElement} section
106 */
107 startExpandSection_: function(section) {
108 if (section.classList.contains('expanded'))
109 return;
110 85
111 // Freeze the scroller and save its position. 86 // Freeze the scroller and save its position.
112 this.listScrollTop_ = this.scroller.scrollTop; 87 this.scroller.listScrollTop_ = this.scroller.scrollTop;
113
114 var scrollerWidth = this.scroller.clientWidth; 88 var scrollerWidth = this.scroller.clientWidth;
115 this.scroller.style.overflow = 'hidden'; 89 this.scroller.style.overflow = 'hidden';
90
116 // Adjust width to compensate for scroller. 91 // Adjust width to compensate for scroller.
117 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth; 92 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
118 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)'; 93 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
119 94
120 // Freezes the section's height so its card can be removed from the flow. 95 this.openSectionTransition_.play()
121 this.freezeSection_(section); 96 .then(function() {
122 97 this.toggleOtherSectionsHidden_(section.section, true);
123 // Expand the section's card to fill the parent. 98 this.scroller.scrollTop = 0;
124 var animationPromise = this.playExpandSection_(section); 99 this.classList.add('showing-subpage');
125 100 this.fire('subpage-expand');
126 animationPromise.then(function() { 101 }.bind(this))
127 this.scroller.scrollTop = 0; 102 .catch(function() {
128 this.toggleOtherSectionsHidden_(section.section, true); 103 this.scroller.scrollTop = this.scroller.listScrollTop_;
129 }.bind(this), function() { 104 }.bind(this))
130 // Animation was canceled; restore the section. 105 .then(function() {
131 this.unfreezeSection_(section); 106 this.scroller.style.width = '';
132 }.bind(this)).then(function() { 107 this.scroller.style.overflow = '';
133 this.scroller.style.overflow = ''; 108 this.openSectionTransition_ = null;
134 this.scroller.style.width = ''; 109 }.bind(this));
135 }.bind(this));
136 }, 110 },
137 111
138 /** 112 /**
139 * Animates the card in |section|, collapsing it back into its section. 113 * Animates the card in |section|, collapsing it back into its section.
140 * @param {!SettingsSectionElement} section 114 * @param {!SettingsSectionElement} section
141 */ 115 */
142 collapseSection: function(section) { 116 collapseSection: function(section) {
143 // If the section's card is still expanding, cancel the expand animation. 117 if (this.closeSectionTransition_) {
144 if (section.classList.contains('expanding')) { 118 assert(this.closeSectionTransition_.section == section);
145 if (this.animations['section']) { 119 this.closeSectionTransition_.cancel();
146 this.cancelAnimation('section');
147 } else {
148 // The animation must have finished but its promise hasn't finished
149 // resolving; try again asynchronously.
150 this.async(function() {
151 this.collapseSection(section);
152 });
153 }
154 return; 120 return;
155 } 121 }
156 122
157 if (!section.classList.contains('expanded')) 123 if (this.openSectionTransition_) {
124 assert(this.openSectionTransition_.section == section);
125 this.openSectionTransition_.cancel();
158 return; 126 return;
127 }
159 128
160 this.toggleOtherSectionsHidden_(section.section, false); 129 assert(section.classList.contains('expanded'));
161 130
162 var scrollerWidth = this.scroller.clientWidth; 131 var scrollerWidth = this.scroller.clientWidth;
163 this.scroller.style.overflow = 'hidden'; 132 this.scroller.style.overflow = 'hidden';
133
164 // Adjust width to compensate for scroller. 134 // Adjust width to compensate for scroller.
135 // TODO(michaelpg): Minimize horizontal motion when scrollbar changes for
136 // the common cases.
165 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth; 137 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
166 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)'; 138 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
167 139
168 this.playCollapseSection_(section).then(function() { 140 // Allow time for the dom-ifs in settings-main to re-render.
169 this.unfreezeSection_(section); 141 // TODO(michaelpg): Use a readiness signal (e.g., from the router) rather
170 this.scroller.style.overflow = ''; 142 // than firing events for settings-main and running this function async.
171 this.scroller.style.width = ''; 143 this.fire('subpage-collapsing');
172 section.classList.remove('collapsing'); 144 this.async(function() {
173 }.bind(this)); 145 // Set up the close transition first to take the section out of the flow
174 }, 146 // before showing everything.
147 this.closeSectionTransition_ =
148 new settings.CloseSectionTransition(section, this.scroller);
149 this.closeSectionTransition_.setUp();
175 150
176 /** 151 this.toggleOtherSectionsHidden_(section.section, false);
177 * Freezes a section's height so its card can be removed from the flow without 152 this.classList.remove('showing-subpage');
178 * affecting the layout of the surrounding sections. 153 this.scroller.scrollTop = this.scroller.listScrollTop_;
179 * @param {!SettingsSectionElement} section
180 * @private
181 */
182 freezeSection_: function(section) {
183 var card = section.$.card;
184 section.style.height = section.clientHeight + 'px';
185 154
186 var cardHeight = card.offsetHeight; 155 this.closeSectionTransition_.play()
187 var cardWidth = card.offsetWidth; 156 .catch(function() {
188 // If the section is not displayed yet (e.g., navigated directly to a 157 this.fire('subpage-expand');
189 // sub-page), cardHeight and cardWidth are 0, so do not set the height or 158 }.bind(this))
190 // width explicitly. 159 .then(function() {
191 // TODO(michaelpg): Improve this logic when refactoring 160 this.scroller.style.overflow = '';
192 // settings-animated-pages. 161 this.scroller.style.width = '';
193 if (cardHeight && cardWidth) { 162 this.closeSectionTransition_ = null;
194 // TODO(michaelpg): Temporary hack to store the height the section should 163 }.bind(this));
195 // collapse to when it closes.
196 card.origHeight_ = cardHeight;
197
198 card.style.height = cardHeight + 'px';
199 card.style.width = cardWidth + 'px';
200 } else {
201 // Set an invalid value so we don't try to use it later.
202 card.origHeight_ = NaN;
203 }
204
205 // Place the section's card at its current position but removed from the
206 // flow.
207 card.style.top = card.getBoundingClientRect().top + 'px';
208 section.classList.add('frozen');
209 },
210
211 /**
212 * After freezeSection_, restores the section to its normal height.
213 * @param {!SettingsSectionElement} section
214 * @private
215 */
216 unfreezeSection_: function(section) {
217 if (!section.classList.contains('frozen'))
218 return;
219 var card = section.$.card;
220 section.classList.remove('frozen');
221 card.style.top = '';
222 card.style.height = '';
223 card.style.width = '';
224 section.style.height = '';
225 },
226
227 /**
228 * Expands the card in |section| to fill the page.
229 * @param {!SettingsSectionElement} section
230 * @return {!Promise}
231 * @private
232 */
233 playExpandSection_: function(section) {
234 var card = section.$.card;
235
236 // The card should start at the top of the page.
237 var targetTop = this.scroller.getBoundingClientRect().top;
238
239 section.classList.add('expanding');
240
241 // Expand the card, using minHeight. (The card must span the container's
242 // client height, so it must be at least 100% in case the card is too short.
243 // If the card is already taller than the container's client height, we
244 // don't want to shrink the card to 100% or the content will overflow, so
245 // we can't use height, and animating height wouldn't look right anyway.)
246 var keyframes = [{
247 top: card.style.top,
248 minHeight: card.style.height,
249 easing: EASING_FUNCTION,
250 }, {
251 top: targetTop + 'px',
252 minHeight: 'calc(100% - ' + targetTop + 'px)',
253 }];
254 var options = /** @type {!KeyframeEffectOptions} */({
255 duration: EXPAND_DURATION
256 }); 164 });
257 // TODO(michaelpg): Change elevation of sections.
258 var promise;
259 if (keyframes[0].top && keyframes[0].minHeight)
260 promise = this.animateElement('section', card, keyframes, options);
261 else
262 promise = Promise.resolve();
263
264 promise.then(function() {
265 section.classList.add('expanded');
266 card.style.top = '';
267 this.style.margin = 'auto';
268 section.$.header.hidden = true;
269 section.style.height = '';
270 }.bind(this), function() {
271 // The animation was canceled; catch the error and continue.
272 }).then(function() {
273 // Whether finished or canceled, clean up the animation.
274 section.classList.remove('expanding');
275 card.style.height = '';
276 card.style.width = '';
277 });
278
279 return promise;
280 },
281
282 /**
283 * Collapses the card in |section| back to its normal position.
284 * @param {!SettingsSectionElement} section
285 * @return {!Promise}
286 * @private
287 */
288 playCollapseSection_: function(section) {
289 var card = section.$.card;
290
291 this.style.margin = '';
292 section.$.header.hidden = false;
293
294 var startingTop = this.scroller.getBoundingClientRect().top;
295
296 var cardHeightStart = card.clientHeight;
297 var cardWidthStart = card.clientWidth;
298
299 section.classList.add('collapsing');
300 section.classList.remove('expanding', 'expanded');
301
302 // If we navigated here directly, we don't know the original height of the
303 // section, so we skip the animation.
304 // TODO(michaelpg): remove this condition once sliding is implemented.
305 if (isNaN(card.origHeight_))
306 return Promise.resolve();
307
308 // Restore the section to its proper height to make room for the card.
309 section.style.height = section.clientHeight + card.origHeight_ + 'px';
310
311 // TODO(michaelpg): this should be in collapseSection(), but we need to wait
312 // until the full page height is available (setting the section height).
313 this.scroller.scrollTop = this.listScrollTop_;
314
315 // The card is unpositioned, so use its position as the ending state,
316 // but account for scroll.
317 var targetTop = card.getBoundingClientRect().top - this.scroller.scrollTop;
318
319 // Account for the section header.
320 var headerStyle = getComputedStyle(section.$.header);
321 targetTop += section.$.header.offsetHeight +
322 parseInt(headerStyle.marginBottom, 10) +
323 parseInt(headerStyle.marginTop, 10);
324
325 var keyframes = [{
326 top: startingTop + 'px',
327 minHeight: cardHeightStart + 'px',
328 easing: EASING_FUNCTION,
329 }, {
330 top: targetTop + 'px',
331 minHeight: card.origHeight_ + 'px',
332 }];
333 var options = /** @type {!KeyframeEffectOptions} */({
334 duration: EXPAND_DURATION
335 });
336
337 card.style.width = cardWidthStart + 'px';
338 var promise = this.animateElement('section', card, keyframes, options);
339 promise.then(function() {
340 card.style.width = '';
341 });
342 return promise;
343 }, 165 },
344 }; 166 };
345 167
346 168
347 /** @polymerBehavior */ 169 /** @polymerBehavior */
348 var MainPageBehavior = [ 170 var MainPageBehavior = [
349 TransitionBehavior,
350 MainPageBehaviorImpl 171 MainPageBehaviorImpl
351 ]; 172 ];
352 173
353 174
354 /** 175 /**
355 * TODO(michaelpg): integrate slide animations. 176 * TODO(michaelpg): integrate slide animations.
356 * @polymerBehavior RoutableBehavior 177 * @polymerBehavior RoutableBehavior
357 */ 178 */
358 var RoutableBehaviorImpl = { 179 var RoutableBehaviorImpl = {
359 properties: { 180 properties: {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 this.$$('[section=' + section + ']')); 241 this.$$('[section=' + section + ']'));
421 }, 242 },
422 }; 243 };
423 244
424 245
425 /** @polymerBehavior */ 246 /** @polymerBehavior */
426 var RoutableBehavior = [ 247 var RoutableBehavior = [
427 MainPageBehavior, 248 MainPageBehavior,
428 RoutableBehaviorImpl 249 RoutableBehaviorImpl
429 ]; 250 ];
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698