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

Unified 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: Refactor 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/settings/settings_page/main_page_behavior.js
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index 532418a01d4054c21e630a04ccefc4ce8a719018..8f952456618f9ae719b61fa724ba97f73b4afa92 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Fast out, slow in.
-var EASING_FUNCTION = 'cubic-bezier(0.4, 0, 0.2, 1)';
-var EXPAND_DURATION = 350;
-
/**
* Calls |readyTest| repeatedly until it returns true, then calls
* |readyCallback|.
@@ -24,21 +20,20 @@ function doWhenReady(readyTest, readyCallback) {
}
/**
- * Provides animations to expand and collapse individual sections in a page.
- * Expanded sections take up the full height of the container. At most one
- * section should be expanded at any given time.
+ * Responds to route changes by expanding, collapsing, and scrolling to
+ * sections. Expanded sections take up the full height of the container. At
+ * most one section should be expanded at any given time.
+ * TODO(michaelpg): Combine with RoutableBehaviorImpl.
* @polymerBehavior Polymer.MainPageBehavior
*/
var MainPageBehaviorImpl = {
- /** @type {?Element} The scrolling container. */
- scroller: null,
-
/** @override */
attached: function() {
- if (this.domHost && this.domHost.parentNode.tagName == 'PAPER-HEADER-PANEL')
- this.scroller = this.domHost.parentNode.$.mainContainer;
- else
- this.scroller = document.body; // Used in unit tests.
+ /** @type {!HTMLElement} */
Dan Beam 2016/08/05 00:18:05 indent off
+ this.scroller = (this.domHost && this.domHost.parentNode.tagName ==
Dan Beam 2016/08/05 00:18:05 what's the functional difference here? why are yo
+ 'PAPER-HEADER-PANEL') ?
+ this.domHost.parentNode.$.mainContainer :
Dan Beam 2016/08/05 00:18:05 yeppppppp this'll trigger that presubmit
+ document.body; // Used in unit tests.
},
/**
@@ -55,295 +50,170 @@ var MainPageBehaviorImpl = {
},
/**
- * Animates the card in |section|, expanding it to fill the page.
- * @param {!SettingsSectionElement} section
+ * Transistions to the current route if the |deferredTransition_| flag is set.
Dan Beam 2016/08/05 00:47:07 Transitions
+ * @private
*/
- expandSection: function(section) {
- // If another section's card is expanding, cancel that animation first.
- var expanding = this.$$('.expanding');
- if (expanding) {
- if (expanding == section)
- return;
+ runDeferredTransition_: function() {
Dan Beam 2016/08/05 00:47:06 this makes it seem as if "deferredTransition_" is
+ // TODO(michaelpg): Manage transition lifetime better. crbug.com/624145
+ if (!this.deferredTransition_)
+ return;
+ this.deferredTransition_ = false;
+ this.transitionToRoute_();
+ },
- if (this.animations['section']) {
- // Cancel the animation, then call startExpandSection_.
- this.cancelAnimation('section', function() {
- this.startExpandSection_(section);
- }.bind(this));
- } else {
- // The animation must have finished but its promise hasn't resolved yet.
- // When it resolves, collapse that section's card before expanding
- // this one.
- setTimeout(function() {
- this.collapseSection(
- /** @type {!SettingsSectionElement} */(expanding));
- this.finishAnimation('section', function() {
- this.startExpandSection_(section);
- }.bind(this));
- }.bind(this));
+ /**
+ * Transitions to the current route based on the state of the current page
+ * (i.e., expands and collapses sections as necessary). Must only be called
+ * when no other transition is running or pending.
+ * @private
+ */
+ transitionToRoute_: function() {
+ assert(!this.currentSectionTransition_);
+
+ var expandedSection = /** @type {?SettingsSectionElement} */(
+ this.$$('settings-section.expanded'));
+ if (expandedSection) {
+ if (expandedSection.section == this.currentRoute.section &&
+ this.currentRoute.subpage.length > 0) {
+ // settings-animated-pages controls visibility of subpages in sections.
Dan Beam 2016/08/05 00:47:06 can you describe this in a different, more high-le
+ return;
}
+ // Collapse this section (and check for a deferred transition when
+ // finished).
+ this.collapseSection(expandedSection).then(
+ this.runDeferredTransition_.bind(this));
+
+ // If the new route is a sub-page, we'll have to expand that next.
+ this.deferredTransition_ = this.currentRoute.subpage.length > 0;
return;
}
- if (this.$$('.collapsing') && this.animations['section']) {
- // Finish the collapse animation before expanding.
- this.finishAnimation('section', function() {
- this.startExpandSection_(section);
- }.bind(this));
+ if (this.currentRoute.section.length == 0)
Dan Beam 2016/08/05 00:47:06 nit: can you just use truthiness for section or ot
+ return;
+
+ var section = this.getSection_(this.currentRoute.section);
+ if (!section)
+ return;
+
+ // Expand the section if the route is a subpage (and check for a deferred
+ // transition when finished).
+ if (this.currentRoute.subpage.length > 0) {
+ this.expandSection(section).then(this.runDeferredTransition_.bind(this));
return;
}
Dan Beam 2016/08/05 00:47:06 can you put a comment saying which cases actually
- this.startExpandSection_(section);
+ this.scrollToSection_();
},
/**
- * Helper function to set up and start the expand animation.
+ * Animates the card in |section|, expanding it to fill the page.
* @param {!SettingsSectionElement} section
+ * @return {!Promise} Promise when the transition finishes or cancels.
*/
- startExpandSection_: function(section) {
- if (section.classList.contains('expanded'))
- return;
+ expandSection: function(section) {
+ assert(!this.currentSectionTransition_);
- // Freeze the scroller and save its position.
- this.listScrollTop_ = this.scroller.scrollTop;
+ // The overscroll must not shrink during the transition.
+ this.fire('overscroll-suppress');
+ // Prevent additional scrolling and keep the expanding section clipped.
+ this.scroller.style.overflow = '';
var scrollerWidth = this.scroller.clientWidth;
+ this.mainScrollTop_ = this.scroller.scrollTop;
this.scroller.style.overflow = 'hidden';
- // Adjust width to compensate for scroller.
+
+ // Set the width to account for the missing scrollbar.
var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
- // Freezes the section's height so its card can be removed from the flow.
- this.freezeSection_(section);
+ this.currentSectionTransition_ =
+ new settings.OpenSectionTransition(section, this.scroller);
+
+ return this.currentSectionTransition_.play().then(function(success) {
+ this.currentSectionTransition_ = null;
+ if (success) {
+ // Hide other sections and scroll to the top of the subpage.
+ this.classList.add('showing-subpage');
+ this.toggleOtherSectionsHidden_(section.section, true);
+ this.scroller.scrollTop = 0;
+ // Notify that the page is fully expanded.
+ this.fire('subpage-expand');
+ } else {
+ // The open transition cancelled, so scroll back to our old location.
+ this.scroller.scrollTop = this.mainScrollTop_;
+ }
- // Expand the section's card to fill the parent.
- var animationPromise = this.playExpandSection_(section);
+ // Overscroll can now be recalculated.
+ this.fire('overscroll-recalc');
- animationPromise.then(function() {
- this.scroller.scrollTop = 0;
- this.toggleOtherSectionsHidden_(section.section, true);
- }.bind(this), function() {
- // Animation was canceled; restore the section.
- this.unfreezeSection_(section);
- }.bind(this)).then(function() {
- this.scroller.style.overflow = '';
+ // Restore the scrollbar.
this.scroller.style.width = '';
+ this.scroller.style.overflow = '';
}.bind(this));
},
/**
* Animates the card in |section|, collapsing it back into its section.
* @param {!SettingsSectionElement} section
+ * @return {!Promise} Promise when the transition finishes or cancels.
*/
collapseSection: function(section) {
- // If the section's card is still expanding, cancel the expand animation.
- if (section.classList.contains('expanding')) {
- if (this.animations['section']) {
- this.cancelAnimation('section');
- } else {
- // The animation must have finished but its promise hasn't finished
- // resolving; try again asynchronously.
- this.async(function() {
- this.collapseSection(section);
- });
- }
- return;
- }
-
- if (!section.classList.contains('expanded'))
- return;
-
- this.toggleOtherSectionsHidden_(section.section, false);
+ assert(!this.currentSectionTransition_);
+ assert(section.classList.contains('expanded'));
- var scrollerWidth = this.scroller.clientWidth;
+ // TODO(michaelpg): Minimize horizontal motion when scrollbar changes for
+ // the common cases.
this.scroller.style.overflow = 'hidden';
- // Adjust width to compensate for scroller.
- var scrollbarWidth = this.scroller.clientWidth - scrollerWidth;
- this.scroller.style.width = 'calc(100% - ' + scrollbarWidth + 'px)';
- this.playCollapseSection_(section).then(function() {
- this.unfreezeSection_(section);
- this.scroller.style.overflow = '';
- this.scroller.style.width = '';
- section.classList.remove('collapsing');
- }.bind(this));
- },
+ // Set up the close transition first, which takes the section out of the
+ // flow, before showing everything.
+ this.currentSectionTransition_ =
+ new settings.CloseSectionTransition(section, this.scroller);
+ this.currentSectionTransition_.setUp();
- /**
- * Freezes a section's height so its card can be removed from the flow without
- * affecting the layout of the surrounding sections.
- * @param {!SettingsSectionElement} section
- * @private
- */
- freezeSection_: function(section) {
- var card = section.$.card;
- section.style.height = section.clientHeight + 'px';
-
- var cardHeight = card.offsetHeight;
- var cardWidth = card.offsetWidth;
- // If the section is not displayed yet (e.g., navigated directly to a
- // sub-page), cardHeight and cardWidth are 0, so do not set the height or
- // width explicitly.
- // TODO(michaelpg): Improve this logic when refactoring
- // settings-animated-pages.
- if (cardHeight && cardWidth) {
- // TODO(michaelpg): Temporary hack to store the height the section should
- // collapse to when it closes.
- card.origHeight_ = cardHeight;
-
- card.style.height = cardHeight + 'px';
- card.style.width = cardWidth + 'px';
- } else {
- // Set an invalid value so we don't try to use it later.
- card.origHeight_ = NaN;
- }
-
- // Place the section's card at its current position but removed from the
- // flow.
- card.style.top = card.getBoundingClientRect().top + 'px';
- section.classList.add('frozen');
- },
-
- /**
- * After freezeSection_, restores the section to its normal height.
- * @param {!SettingsSectionElement} section
- * @private
- */
- unfreezeSection_: function(section) {
- if (!section.classList.contains('frozen'))
- return;
- var card = section.$.card;
- section.classList.remove('frozen');
- card.style.top = '';
- card.style.height = '';
- card.style.width = '';
- section.style.height = '';
- },
-
- /**
- * Expands the card in |section| to fill the page.
- * @param {!SettingsSectionElement} section
- * @return {!Promise}
- * @private
- */
- playExpandSection_: function(section) {
- var card = section.$.card;
-
- // The card should start at the top of the page.
- var targetTop = this.scroller.getBoundingClientRect().top;
-
- section.classList.add('expanding');
-
- // Expand the card, using minHeight. (The card must span the container's
- // client height, so it must be at least 100% in case the card is too short.
- // If the card is already taller than the container's client height, we
- // don't want to shrink the card to 100% or the content will overflow, so
- // we can't use height, and animating height wouldn't look right anyway.)
- var keyframes = [{
- top: card.style.top,
- minHeight: card.style.height,
- easing: EASING_FUNCTION,
- }, {
- top: targetTop + 'px',
- minHeight: 'calc(100% - ' + targetTop + 'px)',
- }];
- var options = /** @type {!KeyframeEffectOptions} */({
- duration: EXPAND_DURATION
- });
- // TODO(michaelpg): Change elevation of sections.
- var promise;
- if (keyframes[0].top && keyframes[0].minHeight)
- promise = this.animateElement('section', card, keyframes, options);
- else
- promise = Promise.resolve();
-
- promise.then(function() {
- section.classList.add('expanded');
- card.style.top = '';
- this.style.margin = 'auto';
- section.$.header.hidden = true;
- section.style.height = '';
- }.bind(this), function() {
- // The animation was canceled; catch the error and continue.
- }).then(function() {
- // Whether finished or canceled, clean up the animation.
- section.classList.remove('expanding');
- card.style.height = '';
- card.style.width = '';
- });
-
- return promise;
- },
+ this.toggleOtherSectionsHidden_(section.section, false);
+ this.classList.remove('showing-subpage');
+ this.fire('overscroll-recalc');
- /**
- * Collapses the card in |section| back to its normal position.
- * @param {!SettingsSectionElement} section
- * @return {!Promise}
- * @private
- */
- playCollapseSection_: function(section) {
- var card = section.$.card;
-
- this.style.margin = '';
- section.$.header.hidden = false;
-
- var startingTop = this.scroller.getBoundingClientRect().top;
-
- var cardHeightStart = card.clientHeight;
- var cardWidthStart = card.clientWidth;
-
- section.classList.add('collapsing');
- section.classList.remove('expanding', 'expanded');
-
- // If we navigated here directly, we don't know the original height of the
- // section, so we skip the animation.
- // TODO(michaelpg): remove this condition once sliding is implemented.
- if (isNaN(card.origHeight_))
- return Promise.resolve();
-
- // Restore the section to its proper height to make room for the card.
- section.style.height = section.clientHeight + card.origHeight_ + 'px';
-
- // TODO(michaelpg): this should be in collapseSection(), but we need to wait
- // until the full page height is available (setting the section height).
- this.scroller.scrollTop = this.listScrollTop_;
-
- // The card is unpositioned, so use its position as the ending state,
- // but account for scroll.
- var targetTop = card.getBoundingClientRect().top - this.scroller.scrollTop;
-
- // Account for the section header.
- var headerStyle = getComputedStyle(section.$.header);
- targetTop += section.$.header.offsetHeight +
- parseInt(headerStyle.marginBottom, 10) +
- parseInt(headerStyle.marginTop, 10);
-
- var keyframes = [{
- top: startingTop + 'px',
- minHeight: cardHeightStart + 'px',
- easing: EASING_FUNCTION,
- }, {
- top: targetTop + 'px',
- minHeight: card.origHeight_ + 'px',
- }];
- var options = /** @type {!KeyframeEffectOptions} */({
- duration: EXPAND_DURATION
- });
-
- card.style.width = cardWidthStart + 'px';
- var promise = this.animateElement('section', card, keyframes, options);
- promise.then(function() {
- card.style.width = '';
- });
- return promise;
+ return new Promise(function(resolve, reject) {
+ // Wait for the other sections to show up so we can scroll properly.
+ setTimeout(function() {
+ // Allow overscroll adjustment so it changes now rather than after the
+ // transition has begun.
+
+ var newSection = this.currentRoute.section &&
+ this.getSection_(this.currentRoute.section);
+
+ // Scroll to the section if indicated by the route. TODO(dschuyler): Is
+ // this the right behavior, or should we return to the previous scroll
+ // position?
+ if (newSection)
+ newSection.scrollIntoView();
+ else
+ this.scroller.scrollTop = this.mainScrollTop_;
+
+ this.currentSectionTransition_.play().then(function(success) {
+ this.currentSectionTransition_ = null;
+ if (!success) {
+ // The collapse was canceled, so ensure the page is still set up for
+ // a full-height section.
+ this.fire('subpage-expand');
+ }
+
+ // Restore the scrollbar.
+ this.scroller.style.overflow = '';
+ this.scroller.style.width = '';
Dan Beam 2016/08/05 00:47:06 marginally useful: make a lockScrolling_() and unl
+ resolve();
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
},
};
/** @polymerBehavior */
var MainPageBehavior = [
- TransitionBehavior,
MainPageBehaviorImpl
];
@@ -371,43 +241,42 @@ var RoutableBehaviorImpl = {
function() {
// If the current section changes while we are waiting for the page to
// be ready, scroll to the newest requested section.
- this.getSection_(this.currentRoute.section).scrollIntoView();
+ var section = this.getSection_(this.currentRoute.section);
+ if (section)
+ section.scrollIntoView();
}.bind(this));
},
- /** @private */
+ /**
+ * @param {?settings.Route} newRoute
+ * @param {?settings.Route} oldRoute
+ * @private
+ */
currentRouteChanged_: function(newRoute, oldRoute) {
- var newRouteIsSubpage = newRoute && newRoute.subpage.length;
- var oldRouteIsSubpage = oldRoute && oldRoute.subpage.length;
-
- if (!oldRoute && newRouteIsSubpage) {
+ if (!oldRoute && newRoute && newRoute.subpage.length > 0) {
// Allow the page to load before expanding the section. TODO(michaelpg):
// Time this better when refactoring settings-animated-pages.
- setTimeout(function() {
- var section = this.getSection_(newRoute.section);
- if (section)
- this.expandSection(section);
- }.bind(this));
+ this.deferredTransition_ = true;
+ setTimeout(this.runDeferredTransition_.bind(this));
return;
}
- if (newRouteIsSubpage) {
- if (!oldRouteIsSubpage || newRoute.section != oldRoute.section) {
- var section = this.getSection_(newRoute.section);
- if (section)
- this.expandSection(section);
- }
- } else {
- if (oldRouteIsSubpage) {
- var section = this.getSection_(oldRoute.section);
- if (section)
- this.collapseSection(section);
+ if (this.currentSectionTransition_) {
+ // Transition to the current route when the current one finishes.
+ this.deferredTransition_ = true;
+
+ // If the current transition is opening a section we want to close, cancel
+ // that transition immediately.
+ if (this.currentSectionTransition_ instanceof
+ settings.OpenSectionTransition &&
+ (this.currentSectionTransition_.section != newRoute.section ||
+ newRoute.subpage.length == 0)) {
Dan Beam 2016/08/05 00:47:07 can you use some temporary variables for this?
+ this.currentSectionTransition_.cancel();
}
-
- // Scrolls to the section if this main page contains the route's section.
- if (newRoute && newRoute.section && this.getSection_(newRoute.section))
- this.scrollToSection_();
+ return;
}
+
+ this.transitionToRoute_();
},
/**

Powered by Google App Engine
This is Rietveld 408576698