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

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

Issue 2215213002: Move settings-section expand/collapse animations to settings-section (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@ConsolidateStyles
Patch Set: remove some holdovers Created 4 years, 4 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() {
19 if (readyTest()) { 15 if (readyTest()) {
20 clearInterval(intervalId); 16 clearInterval(intervalId);
21 readyCallback(); 17 readyCallback();
22 } 18 }
23 }, 10); 19 }, 10);
24 } 20 }
25 21
26 /** 22 /**
27 * Provides animations to expand and collapse individual sections in a page. 23 * Provides animations to expand and collapse individual sections in a page.
28 * Expanded sections take up the full height of the container. At most one 24 * Expanded sections take up the full height of the container. At most one
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 /** @type {?Element} The scrolling container. */ 29 /** @type {?HTMLElement} The scrolling container. */
34 scroller: null, 30 scroller: null,
35 31
36 /** @override */ 32 /** @override */
37 attached: function() { 33 attached: function() {
38 if (this.domHost && this.domHost.parentNode.tagName == 'PAPER-HEADER-PANEL') 34 if (this.domHost && this.domHost.parentNode.tagName == 'PAPER-HEADER-PANEL')
39 this.scroller = this.domHost.parentNode.$.mainContainer; 35 this.scroller = this.domHost.parentNode./* presubmit */$.mainContainer;
Dan Beam 2016/08/05 04:14:38 scroller
michaelpg 2016/08/05 05:16:08 Done (also in a standalone CL)
40 else 36 else
41 this.scroller = document.body; // Used in unit tests. 37 this.scroller = document.body; // Used in unit tests.
42 }, 38 },
43 39
44 /** 40 /**
45 * Hides or unhides the sections not being expanded. 41 * Hides or unhides the sections not being expanded.
46 * @param {string} sectionName The section to keep visible. 42 * @param {string} sectionName The section to keep visible.
47 * @param {boolean} hidden Whether the sections should be hidden. 43 * @param {boolean} hidden Whether the sections should be hidden.
48 * @private 44 * @private
49 */ 45 */
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 } 91 }
96 92
97 this.startExpandSection_(section); 93 this.startExpandSection_(section);
98 }, 94 },
99 95
100 /** 96 /**
101 * Helper function to set up and start the expand animation. 97 * Helper function to set up and start the expand animation.
102 * @param {!SettingsSectionElement} section 98 * @param {!SettingsSectionElement} section
103 */ 99 */
104 startExpandSection_: function(section) { 100 startExpandSection_: function(section) {
105 if (section.classList.contains('expanded')) 101 if (!section.canAnimateExpand())
106 return; 102 return;
107 103
108 // Freeze the scroller and save its position. 104 // Freeze the scroller and save its position.
109 this.listScrollTop_ = this.scroller.scrollTop; 105 this.listScrollTop_ = this.scroller.scrollTop;
110 106
111 var scrollerWidth = this.scroller.clientWidth; 107 var scrollerWidth = this.scroller.clientWidth;
112 this.scroller.style.overflow = 'hidden'; 108 this.scroller.style.overflow = 'hidden';
113 // Adjust width to compensate for scroller. 109 // Adjust width to compensate for scroller.
114 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth; 110 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
115 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)'; 111 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
116 112
117 // Freezes the section's height so its card can be removed from the flow. 113 // Freezes the section's height so its card can be removed from the flow.
118 this.freezeSection_(section); 114 section.freezeDimensions();
Dan Beam 2016/08/05 04:14:38 setFrozen(true/false)?
michaelpg 2016/08/05 05:16:08 Done.
119 115
120 // Expand the section's card to fill the parent. 116 // Expand the section's card to fill the parent.
121 var animationPromise = this.playExpandSection_(section); 117 var animationPromise = this.playExpandSection_(section);
122 118
123 animationPromise.then(function() { 119 animationPromise.then(function() {
124 this.scroller.scrollTop = 0; 120 this.scroller.scrollTop = 0;
125 this.toggleOtherSectionsHidden_(section.section, true); 121 this.toggleOtherSectionsHidden_(section.section, true);
126 }.bind(this), function() { 122 }.bind(this), function() {
127 // Animation was canceled; restore the section. 123 // Animation was canceled; restore the section.
128 this.unfreezeSection_(section); 124 section.unfreezeDimensions();
129 }.bind(this)).then(function() { 125 }.bind(this)).then(function() {
130 this.scroller.style.overflow = ''; 126 this.scroller.style.overflow = '';
131 this.scroller.style.width = ''; 127 this.scroller.style.width = '';
132 }.bind(this)); 128 }.bind(this));
133 }, 129 },
134 130
135 /** 131 /**
136 * Animates the card in |section|, collapsing it back into its section. 132 * Animates the card in |section|, collapsing it back into its section.
137 * @param {!SettingsSectionElement} section 133 * @param {!SettingsSectionElement} section
138 */ 134 */
139 collapseSection: function(section) { 135 collapseSection: function(section) {
140 // If the section's card is still expanding, cancel the expand animation. 136 // If the section's card is still expanding, cancel the expand animation.
141 if (section.classList.contains('expanding')) { 137 if (section.classList.contains('expanding')) {
142 if (this.animations['section']) { 138 if (this.animations['section']) {
143 this.cancelAnimation('section'); 139 this.cancelAnimation('section');
144 } else { 140 } else {
145 // The animation must have finished but its promise hasn't finished 141 // The animation must have finished but its promise hasn't finished
146 // resolving; try again asynchronously. 142 // resolving; try again asynchronously.
147 this.async(function() { 143 this.async(function() {
148 this.collapseSection(section); 144 this.collapseSection(section);
149 }); 145 });
150 } 146 }
151 return; 147 return;
152 } 148 }
153 149
154 if (!section.classList.contains('expanded')) 150 if (!section.canAnimateCollapse())
155 return; 151 return;
156 152
157 this.toggleOtherSectionsHidden_(section.section, false); 153 this.toggleOtherSectionsHidden_(section.section, false);
158 154
159 var scrollerWidth = this.scroller.clientWidth; 155 var scrollerWidth = this.scroller.clientWidth;
160 this.scroller.style.overflow = 'hidden'; 156 this.scroller.style.overflow = 'hidden';
161 // Adjust width to compensate for scroller. 157 // Adjust width to compensate for scroller.
162 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth; 158 var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
163 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)'; 159 this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
164 160
165 this.playCollapseSection_(section).then(function() { 161 this.playCollapseSection_(section).then(function() {
166 this.unfreezeSection_(section); 162 section.unfreezeDimensions();
167 this.scroller.style.overflow = ''; 163 this.scroller.style.overflow = '';
168 this.scroller.style.width = ''; 164 this.scroller.style.width = '';
169 section.classList.remove('collapsing'); 165 section.classList.remove('collapsing');
170 }.bind(this)); 166 }.bind(this));
171 }, 167 },
172 168
173 /** 169 /**
174 * Freezes a section's height so its card can be removed from the flow without
175 * affecting the layout of the surrounding sections.
176 * @param {!SettingsSectionElement} section
177 * @private
178 */
179 freezeSection_: function(section) {
180 var card = section.$.card;
181 section.style.height = section.clientHeight + 'px';
182
183 var cardHeight = card.offsetHeight;
184 var cardWidth = card.offsetWidth;
185 // If the section is not displayed yet (e.g., navigated directly to a
186 // sub-page), cardHeight and cardWidth are 0, so do not set the height or
187 // width explicitly.
188 // TODO(michaelpg): Improve this logic when refactoring
189 // settings-animated-pages.
190 if (cardHeight && cardWidth) {
191 // TODO(michaelpg): Temporary hack to store the height the section should
192 // collapse to when it closes.
193 card.origHeight_ = cardHeight;
194
195 card.style.height = cardHeight + 'px';
196 card.style.width = cardWidth + 'px';
197 } else {
198 // Set an invalid value so we don't try to use it later.
199 card.origHeight_ = NaN;
200 }
201
202 // Place the section's card at its current position but removed from the
203 // flow.
204 card.style.top = card.getBoundingClientRect().top + 'px';
205 section.classList.add('frozen');
206 },
207
208 /**
209 * After freezeSection_, restores the section to its normal height.
210 * @param {!SettingsSectionElement} section
211 * @private
212 */
213 unfreezeSection_: function(section) {
214 if (!section.classList.contains('frozen'))
215 return;
216 var card = section.$.card;
217 section.classList.remove('frozen');
218 card.style.top = '';
219 card.style.height = '';
220 card.style.width = '';
221 section.style.height = '';
222 },
223
224 /**
225 * Expands the card in |section| to fill the page. 170 * Expands the card in |section| to fill the page.
226 * @param {!SettingsSectionElement} section 171 * @param {!SettingsSectionElement} section
227 * @return {!Promise} 172 * @return {!Promise}
228 * @private 173 * @private
229 */ 174 */
230 playExpandSection_: function(section) { 175 playExpandSection_: function(section) {
231 var card = section.$.card; 176 // We must be attached.
177 assert(this.scroller);
232 178
233 // The card should start at the top of the page. 179 var promise;
234 var targetTop = this.scroller.getBoundingClientRect().top; 180 var animationConfig = section.animateExpand(this.scroller);
181 if (animationConfig) {
182 promise = this.animateElement('section', animationConfig.card,
183 animationConfig.keyframes, animationConfig.options);
184 } else {
185 promise = Promise.resolve();
186 }
235 187
236 section.classList.add('expanding'); 188 var finished;
237
238 // Expand the card, using minHeight. (The card must span the container's
239 // client height, so it must be at least 100% in case the card is too short.
240 // If the card is already taller than the container's client height, we
241 // don't want to shrink the card to 100% or the content will overflow, so
242 // we can't use height, and animating height wouldn't look right anyway.)
243 var keyframes = [{
244 top: card.style.top,
245 minHeight: card.style.height,
246 easing: EASING_FUNCTION,
247 }, {
248 top: targetTop + 'px',
249 minHeight: 'calc(100% - ' + targetTop + 'px)',
250 }];
251 var options = /** @type {!KeyframeEffectOptions} */({
252 duration: EXPAND_DURATION
253 });
254 // TODO(michaelpg): Change elevation of sections.
255 var promise;
256 if (keyframes[0].top && keyframes[0].minHeight)
257 promise = this.animateElement('section', card, keyframes, options);
258 else
259 promise = Promise.resolve();
260
261 promise.then(function() { 189 promise.then(function() {
262 section.classList.add('expanded'); 190 finished = true;
263 card.style.top = '';
264 this.style.margin = 'auto'; 191 this.style.margin = 'auto';
265 section.$.header.hidden = true;
266 section.style.height = '';
267 }.bind(this), function() { 192 }.bind(this), function() {
268 // The animation was canceled; catch the error and continue. 193 // The animation was canceled; catch the error and continue.
194 finished = false;
269 }).then(function() { 195 }).then(function() {
270 // Whether finished or canceled, clean up the animation. 196 section.cleanUpAnimateExpand(finished);
271 section.classList.remove('expanding');
272 card.style.height = '';
273 card.style.width = '';
274 }); 197 });
275 198
276 return promise; 199 return promise;
277 }, 200 },
278 201
279 /** 202 /**
280 * Collapses the card in |section| back to its normal position. 203 * Collapses the card in |section| back to its normal position.
281 * @param {!SettingsSectionElement} section 204 * @param {!SettingsSectionElement} section
282 * @return {!Promise} 205 * @return {!Promise}
283 * @private 206 * @private
284 */ 207 */
285 playCollapseSection_: function(section) { 208 playCollapseSection_: function(section) {
286 var card = section.$.card; 209 // We must be attached.
210 assert(this.scroller);
287 211
288 this.style.margin = ''; 212 this.style.margin = '';
289 section.$.header.hidden = false;
290 213
291 var startingTop = this.scroller.getBoundingClientRect().top; 214 var promise;
215 var animationConfig =
216 section.animateCollapse(this.scroller, this.listScrollTop_);
217 if (animationConfig) {
218 promise = this.animateElement('section', animationConfig.card,
219 animationConfig.keyframes, animationConfig.options);
220 } else {
221 promise = Promise.resolve();
222 }
292 223
293 var cardHeightStart = card.clientHeight;
294 var cardWidthStart = card.clientWidth;
295
296 section.classList.add('collapsing');
297 section.classList.remove('expanding', 'expanded');
298
299 // If we navigated here directly, we don't know the original height of the
300 // section, so we skip the animation.
301 // TODO(michaelpg): remove this condition once sliding is implemented.
302 if (isNaN(card.origHeight_))
303 return Promise.resolve();
304
305 // Restore the section to its proper height to make room for the card.
306 section.style.height = section.clientHeight + card.origHeight_ + 'px';
307
308 // TODO(michaelpg): this should be in collapseSection(), but we need to wait
309 // until the full page height is available (setting the section height).
310 this.scroller.scrollTop = this.listScrollTop_;
311
312 // The card is unpositioned, so use its position as the ending state,
313 // but account for scroll.
314 var targetTop = card.getBoundingClientRect().top - this.scroller.scrollTop;
315
316 // Account for the section header.
317 var headerStyle = getComputedStyle(section.$.header);
318 targetTop += section.$.header.offsetHeight +
319 parseInt(headerStyle.marginBottom, 10) +
320 parseInt(headerStyle.marginTop, 10);
321
322 var keyframes = [{
323 top: startingTop + 'px',
324 minHeight: cardHeightStart + 'px',
325 easing: EASING_FUNCTION,
326 }, {
327 top: targetTop + 'px',
328 minHeight: card.origHeight_ + 'px',
329 }];
330 var options = /** @type {!KeyframeEffectOptions} */({
331 duration: EXPAND_DURATION
332 });
333
334 card.style.width = cardWidthStart + 'px';
335 var promise = this.animateElement('section', card, keyframes, options);
336 promise.then(function() { 224 promise.then(function() {
337 card.style.width = ''; 225 section.cleanUpAnimateCollapse(true);
226 }, function() {
227 section.cleanUpAnimateCollapse(false);
338 }); 228 });
339 return promise; 229 return promise;
340 }, 230 },
341 }; 231 };
342 232
343 233
344 /** @polymerBehavior */ 234 /** @polymerBehavior */
345 var MainPageBehavior = [ 235 var MainPageBehavior = [
346 TransitionBehavior, 236 TransitionBehavior,
347 MainPageBehaviorImpl 237 MainPageBehaviorImpl
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 this.$$('[section=' + section + ']')); 311 this.$$('[section=' + section + ']'));
422 }, 312 },
423 }; 313 };
424 314
425 315
426 /** @polymerBehavior */ 316 /** @polymerBehavior */
427 var RoutableBehavior = [ 317 var RoutableBehavior = [
428 MainPageBehavior, 318 MainPageBehavior,
429 RoutableBehaviorImpl 319 RoutableBehaviorImpl
430 ]; 320 ];
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698