Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/browser/model/animation.ts
4798 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { getActiveWindow } from '../../../../../base/browser/dom.js';
7
import { ISettableObservable, observableValue, ITransaction, IReader, observableSignal } from '../../../../../base/common/observable.js';
8
9
export class AnimatedValue {
10
public static const(value: number): AnimatedValue {
11
return new AnimatedValue(value, value, 0);
12
}
13
14
public readonly startTimeMs = Date.now();
15
16
constructor(
17
public readonly startValue: number,
18
public readonly endValue: number,
19
public readonly durationMs: number,
20
private readonly _interpolationFunction: InterpolationFunction = easeOutExpo,
21
) {
22
if (startValue === endValue) {
23
this.durationMs = 0;
24
}
25
}
26
27
isFinished(): boolean {
28
return Date.now() >= this.startTimeMs + this.durationMs;
29
}
30
31
getValue(): number {
32
const timePassed = Date.now() - this.startTimeMs;
33
if (timePassed >= this.durationMs) {
34
return this.endValue;
35
}
36
const value = this._interpolationFunction(timePassed, this.startValue, this.endValue - this.startValue, this.durationMs);
37
return value;
38
}
39
}
40
41
type InterpolationFunction = (passedTime: number, start: number, length: number, totalDuration: number) => number;
42
43
export function easeOutExpo(passedTime: number, start: number, length: number, totalDuration: number): number {
44
return passedTime === totalDuration
45
? start + length
46
: length * (-Math.pow(2, -10 * passedTime / totalDuration) + 1) + start;
47
}
48
49
export function easeOutCubic(passedTime: number, start: number, length: number, totalDuration: number): number {
50
return length * ((passedTime = passedTime / totalDuration - 1) * passedTime * passedTime + 1) + start;
51
}
52
53
export function linear(passedTime: number, start: number, length: number, totalDuration: number): number {
54
return length * passedTime / totalDuration + start;
55
}
56
57
export class ObservableAnimatedValue {
58
public static const(value: number): ObservableAnimatedValue {
59
return new ObservableAnimatedValue(AnimatedValue.const(value));
60
}
61
62
private readonly _value: ISettableObservable<AnimatedValue>;
63
64
constructor(
65
initialValue: AnimatedValue,
66
) {
67
this._value = observableValue(this, initialValue);
68
}
69
70
setAnimation(value: AnimatedValue, tx: ITransaction | undefined): void {
71
this._value.set(value, tx);
72
}
73
74
changeAnimation(fn: (prev: AnimatedValue) => AnimatedValue, tx: ITransaction | undefined): void {
75
const value = fn(this._value.get());
76
this._value.set(value, tx);
77
}
78
79
getValue(reader: IReader | undefined): number {
80
const value = this._value.read(reader);
81
if (!value.isFinished()) {
82
AnimationFrameScheduler.instance.invalidateOnNextAnimationFrame(reader);
83
}
84
return value.getValue();
85
}
86
}
87
88
export class AnimationFrameScheduler {
89
public static instance = new AnimationFrameScheduler();
90
91
private readonly _counter = observableSignal(this);
92
93
private _isScheduled = false;
94
95
public invalidateOnNextAnimationFrame(reader: IReader | undefined): void {
96
this._counter.read(reader);
97
if (!this._isScheduled) {
98
this._isScheduled = true;
99
getActiveWindow().requestAnimationFrame(() => {
100
this._isScheduled = false;
101
this._update();
102
});
103
}
104
}
105
106
private _update(): void {
107
this._counter.trigger(undefined);
108
}
109
}
110
111