提交 c2803e3c 编写于 作者: A Alex Dima

Have two animations when jumping at large distances (#33347)

上级 ca2d5dd0
......@@ -163,6 +163,9 @@ export interface INewScrollDimensions {
export interface IScrollPosition {
readonly scrollLeft: number;
readonly scrollTop: number;
readonly width: number;
readonly height: number;
}
export interface INewScrollPosition {
scrollLeft?: number;
......@@ -326,7 +329,7 @@ export class Scrollable extends Disposable {
}
}
class SmoothScrollingUpdate implements IScrollPosition {
export class SmoothScrollingUpdate {
public readonly scrollLeft: number;
public readonly scrollTop: number;
......@@ -340,7 +343,27 @@ class SmoothScrollingUpdate implements IScrollPosition {
}
class SmoothScrollingOperation {
export interface IAnimation {
(completion: number): number;
}
function createEaseOutCubic(from: number, to: number): IAnimation {
const delta = to - from;
return function (completion: number): number {
return from + delta * easeOutCubic(completion);
};
}
function createComposed(a: IAnimation, b: IAnimation, cut: number): IAnimation {
return function (completion: number): number {
if (completion < cut) {
return a(completion / cut);
}
return b((completion - cut) / (1 - cut));
};
}
export class SmoothScrollingOperation {
public readonly from: IScrollPosition;
public to: IScrollPosition;
......@@ -348,15 +371,40 @@ class SmoothScrollingOperation {
private readonly _startTime: number;
public animationFrameDisposable: IDisposable;
private constructor(from: IScrollPosition, to: IScrollPosition, duration: number) {
private scrollLeft: IAnimation;
private scrollTop: IAnimation;
protected constructor(from: IScrollPosition, to: IScrollPosition, startTime: number, duration: number) {
this.from = from;
this.to = to;
// +10 / -10 : pretend the animation already started for a quicker response to a scroll request
this.duration = duration + 10;
this._startTime = Date.now() - 10;
this.duration = duration;
this._startTime = startTime;
this.animationFrameDisposable = null;
this._initAnimations();
}
private _initAnimations(): void {
this.scrollLeft = this._initAnimation(this.from.scrollLeft, this.to.scrollLeft, this.to.width);
this.scrollTop = this._initAnimation(this.from.scrollTop, this.to.scrollTop, this.to.height);
}
private _initAnimation(from: number, to: number, viewportSize: number): IAnimation {
const delta = Math.abs(from - to);
if (delta > 2.5 * viewportSize) {
let stop1: number, stop2: number;
if (from < to) {
// scroll to 75% of the viewportSize
stop1 = from + 0.75 * viewportSize;
stop2 = to - 0.75 * viewportSize;
} else {
stop1 = from - 0.75 * viewportSize;
stop2 = to + 0.75 * viewportSize;
}
return createComposed(createEaseOutCubic(from, stop1), createEaseOutCubic(stop2, to), 0.33);
}
return createEaseOutCubic(from, to);
}
public dispose(): void {
......@@ -368,15 +416,19 @@ class SmoothScrollingOperation {
public acceptScrollDimensions(state: ScrollState): void {
this.to = state.withScrollPosition(this.to);
this._initAnimations();
}
public tick(): SmoothScrollingUpdate {
const completion = (Date.now() - this._startTime) / this.duration;
return this._tick(Date.now());
}
protected _tick(now: number): SmoothScrollingUpdate {
const completion = (now - this._startTime) / this.duration;
if (completion < 1) {
const t = easeOutCubic(completion);
const newScrollLeft = this.from.scrollLeft + (this.to.scrollLeft - this.from.scrollLeft) * t;
const newScrollTop = this.from.scrollTop + (this.to.scrollTop - this.from.scrollTop) * t;
const newScrollLeft = this.scrollLeft(completion);
const newScrollTop = this.scrollTop(completion);
return new SmoothScrollingUpdate(newScrollLeft, newScrollTop, false);
}
......@@ -388,14 +440,18 @@ class SmoothScrollingOperation {
}
public static start(from: IScrollPosition, to: IScrollPosition, duration: number): SmoothScrollingOperation {
return new SmoothScrollingOperation(from, to, duration);
// +10 / -10 : pretend the animation already started for a quicker response to a scroll request
duration = duration + 10;
const startTime = Date.now() - 10;
return new SmoothScrollingOperation(from, to, startTime, duration);
}
}
function easeInCubic(t) {
function easeInCubic(t: number) {
return Math.pow(t, 3);
}
function easeOutCubic(t) {
function easeOutCubic(t: number) {
return 1 - easeInCubic(1 - t);
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { SmoothScrollingOperation, SmoothScrollingUpdate } from 'vs/base/common/scrollable';
class TestSmoothScrollingOperation extends SmoothScrollingOperation {
constructor(from: number, to: number, viewportSize: number, startTime: number, duration: number) {
duration = duration + 10;
startTime = startTime - 10;
super(
{ scrollLeft: 0, scrollTop: from, width: 0, height: viewportSize },
{ scrollLeft: 0, scrollTop: to, width: 0, height: viewportSize },
startTime,
duration
);
}
public testTick(now: number): SmoothScrollingUpdate {
return this._tick(now);
}
}
suite('SmoothScrollingOperation', () => {
const VIEWPORT_HEIGHT = 800;
const ANIMATION_DURATION = 125;
const LINE_HEIGHT = 20;
function extractLines(scrollable: TestSmoothScrollingOperation, now: number): [number, number] {
let scrollTop = scrollable.testTick(now).scrollTop;
let scrollBottom = scrollTop + VIEWPORT_HEIGHT;
const startLineNumber = Math.floor(scrollTop / LINE_HEIGHT);
const endLineNumber = Math.ceil(scrollBottom / LINE_HEIGHT);
return [startLineNumber, endLineNumber];
}
function simulateSmoothScroll(from: number, to: number): [number, number][] {
const scrollable = new TestSmoothScrollingOperation(from, to, VIEWPORT_HEIGHT, 0, ANIMATION_DURATION);
let result: [number, number][] = [], resultLen = 0;
result[resultLen++] = extractLines(scrollable, 0);
result[resultLen++] = extractLines(scrollable, 25);
result[resultLen++] = extractLines(scrollable, 50);
result[resultLen++] = extractLines(scrollable, 75);
result[resultLen++] = extractLines(scrollable, 100);
result[resultLen++] = extractLines(scrollable, 125);
return result;
}
function assertSmoothScroll(from: number, to: number, expected: [number, number][]): void {
const actual = simulateSmoothScroll(from, to);
assert.deepEqual(actual, expected);
}
test('scroll 25 lines (40 fit)', () => {
assertSmoothScroll(0, 500, [
[5, 46],
[14, 55],
[20, 61],
[23, 64],
[24, 65],
[25, 65],
]);
});
test('scroll 75 lines (40 fit)', () => {
assertSmoothScroll(0, 1500, [
[15, 56],
[44, 85],
[62, 103],
[71, 112],
[74, 115],
[75, 115],
]);
});
test('scroll 100 lines (40 fit)', () => {
assertSmoothScroll(0, 2000, [
[20, 61],
[59, 100],
[82, 123],
[94, 135],
[99, 140],
[100, 140],
]);
});
test('scroll 125 lines (40 fit)', () => {
assertSmoothScroll(0, 2500, [
[16, 57],
[29, 70],
[107, 148],
[119, 160],
[124, 165],
[125, 165],
]);
});
test('scroll 500 lines (40 fit)', () => {
assertSmoothScroll(0, 10000, [
[16, 57],
[29, 70],
[482, 523],
[494, 535],
[499, 540],
[500, 540],
]);
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册