This repository has been archived by the owner on May 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
friction.ts
117 lines (105 loc) · 3.19 KB
/
friction.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { getMaxDistanceFriction, frictionForceV, applyForce } from '../forces';
import type {
AnimatingElement,
StatefulAnimatingElement,
AnimationGroup,
} from './types';
import { group } from './group';
export interface FrictionConfig {
mu: number;
mass: number;
initialVelocity: number;
}
// A function to apply the force of friction on each step in requestAnimationFrame.
function applyFrictionForceForStep({
state: { mover },
config,
}: StatefulAnimatingElement<FrictionConfig>) {
const force = frictionForceV({
mu: config.mu,
mass: config.mass,
velocity: mover.velocity,
});
return applyForce({ force, entity: mover, time: 0.001 });
}
/**
* A function to check the current play state of the friction animation
* and potentially reverse it.
*
* If a friction animation has repeat specified in its config _and_
* has reached its physics stopping condition of velocity 0, we reset
* the initial parameters and reverse the direction of the animation.
*/
function checkReverseFrictionPlayState({
state,
config,
repeatType,
}: StatefulAnimatingElement<FrictionConfig>) {
const isOvershootingForward =
state.mover.velocity[0] <= 0 && state.playState === 'forward';
const isOvershootingReverse =
state.mover.velocity[0] >= 0 && state.playState === 'reverse';
if (isOvershootingForward && repeatType === 'loop') {
state.mover = {
...state.mover,
acceleration: [0, 0],
velocity: [config.initialVelocity, 0],
position: [0, 0],
};
state.repeatCount++;
} else if (isOvershootingForward) {
state.mover = {
...state.mover,
acceleration: [0, 0],
velocity: [config.initialVelocity * -1, 0],
position: [state.maxDistance, 0],
};
state.playState = 'reverse';
state.repeatCount++;
} else if (isOvershootingReverse) {
state.mover = {
...state.mover,
acceleration: [0, 0],
velocity: [config.initialVelocity, 0],
position: [0, 0],
};
state.playState = 'forward';
state.repeatCount++;
}
}
// A function to check whether or not the mover has come to rest.
function checkFrictionStoppingCondition({
state,
}: StatefulAnimatingElement<FrictionConfig>) {
return state.mover.velocity[0] <= 0;
}
// A function to take in a set of elements and begin animating them
// according to the force of friction.
export function frictionGroup(
elements: AnimatingElement<FrictionConfig>[]
): AnimationGroup<FrictionConfig> {
const initialState = (
element: AnimatingElement<FrictionConfig>
): StatefulAnimatingElement<FrictionConfig>['state'] => ({
mover: {
mass: element.config.mass,
acceleration: [0, 0],
velocity: [element.config.initialVelocity, 0],
position: [0, 0],
},
playState: 'forward',
maxDistance: getMaxDistanceFriction({
mu: element.config.mu,
initialVelocity: element.config.initialVelocity,
}),
complete: false,
paused: !!element.pause,
delayed: !!element.delay,
repeatCount: -1,
});
return group(elements, initialState, {
checkReversePlayState: checkReverseFrictionPlayState,
applyForceForStep: applyFrictionForceForStep,
checkStoppingCondition: checkFrictionStoppingCondition,
});
}