Commit 38d46a35 authored by John Lee's avatar John Lee Committed by Commit Bot

WebUI Tab Strip: Animate scroll position

Since there is no native way to animate scroll with custom easing,
speeds, and offsets, a manual animation is needed to animate the scroll
position.

This CL uses requestAnimationFrame to recursively update the scrollLeft
property of the scrolling container. Depending on the refresh rate, the
TabList will continuously update its own scroll position based on the
time elapsed until the animation time is over. To customize easing, a
deceleration function is used to generally mimic slowing of the
animation towards the end.

Bug: 1023492
Change-Id: Ib89e01c8f9e1fccd3d62b43fb5e7841a1cc311a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1951544
Commit-Queue: John Lee <johntlee@chromium.org>
Reviewed-by: default avatarDemetrios Papadopoulos <dpapad@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721867}
parent 5d594660
......@@ -24,6 +24,14 @@ import {TabData, TabsApiProxy} from './tabs_api_proxy.js';
*/
const SCROLL_PADDING = 32;
/** @type {boolean} */
let scrollAnimationEnabled = true;
/** @param {boolean} enabled */
export function setScrollAnimationEnabledForTesting(enabled) {
scrollAnimationEnabled = enabled;
}
/**
* @enum {string}
*/
......@@ -57,6 +65,13 @@ class TabListElement extends CustomElement {
*/
this.animationPromises = Promise.resolve();
/**
* The ID of the current animation frame that is in queue to update the
* scroll position.
* @private {?number}
*/
this.currentScrollUpdateFrame_ = null;
/** @private {!Function} */
this.documentVisibilityChangeListener_ = () =>
this.onDocumentVisibilityChange_();
......@@ -159,6 +174,50 @@ class TabListElement extends CustomElement {
this.animationPromises = this.animationPromises.then(() => promise);
}
/**
* @param {number} scrollBy
* @private
*/
animateScrollPosition_(scrollBy) {
if (this.currentScrollUpdateFrame_) {
cancelAnimationFrame(this.currentScrollUpdateFrame_);
this.currentScrollUpdateFrame_ = null;
}
const prevScrollLeft = this.scrollLeft;
if (!scrollAnimationEnabled || !this.tabStripEmbedderProxy_.isVisible()) {
// Do not animate if tab strip is not visible.
this.scrollLeft = prevScrollLeft + scrollBy;
return;
}
const duration = 350;
let startTime;
const onAnimationFrame = (currentTime) => {
const startScroll = this.scrollLeft;
if (!startTime) {
startTime = currentTime;
}
const elapsedRatio = Math.min(1, (currentTime - startTime) / duration);
// The elapsed ratio should be decelerated such that the elapsed time
// of the animation gets less and less further apart as time goes on,
// giving the effect of an animation that slows down towards the end. When
// 0ms has passed, the decelerated ratio should be 0. When the full
// duration has passed, the ratio should be 1.
const deceleratedRatio =
1 - (1 - elapsedRatio) / Math.pow(2, 6 * elapsedRatio);
this.scrollLeft = prevScrollLeft + (scrollBy * deceleratedRatio);
this.currentScrollUpdateFrame_ =
deceleratedRatio < 1 ? requestAnimationFrame(onAnimationFrame) : null;
};
this.currentScrollUpdateFrame_ = requestAnimationFrame(onAnimationFrame);
}
/**
* @param {!Object<string, string>} dictionary
* @private
......@@ -534,7 +593,7 @@ class TabListElement extends CustomElement {
}
}
this.scrollLeft += scrollBy;
this.animateScrollPosition_(scrollBy);
}
/**
......
......@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://tab-strip/tab_list.js';
import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
import {setScrollAnimationEnabledForTesting} from 'chrome://tab-strip/tab_list.js';
import {TabStripEmbedderProxy} from 'chrome://tab-strip/tab_strip_embedder_proxy.js';
import {TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
......@@ -125,6 +124,8 @@ suite('TabList', () => {
testTabStripEmbedderProxy.setVisible(true);
TabStripEmbedderProxy.instance_ = testTabStripEmbedderProxy;
setScrollAnimationEnabledForTesting(false);
tabList = document.createElement('tabstrip-tab-list');
document.body.appendChild(tabList);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment