forked from software-mansion/react-native-reanimated
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TypeScript for animations (software-mansion#2182)
## Description Refactor and transfter of animations module to TypeScript.
- Loading branch information
Showing
28 changed files
with
1,348 additions
and
943 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
export type PrimitiveValue = number | string; | ||
|
||
export interface AnimationObject { | ||
[key: string]: any; | ||
callback: AnimationCallback; | ||
current?: PrimitiveValue; | ||
toValue?: AnimationObject['current']; | ||
startValue?: AnimationObject['current']; | ||
finished?: boolean; | ||
|
||
__prefix?: string; | ||
__suffix?: string; | ||
onFrame: (animation: any, timestamp: Timestamp) => boolean; | ||
onStart: ( | ||
nextAnimation: any, | ||
current: any, | ||
timestamp: Timestamp, | ||
previousAnimation: any | ||
) => void; | ||
} | ||
|
||
export interface Animation<T extends AnimationObject> extends AnimationObject { | ||
onFrame: (animation: T, timestamp: Timestamp) => boolean; | ||
onStart: ( | ||
nextAnimation: T, | ||
current: T extends NumericAnimation ? number : PrimitiveValue, | ||
timestamp: Timestamp, | ||
previousAnimation: T | ||
) => void; | ||
} | ||
|
||
export interface NumericAnimation { | ||
current?: number; | ||
} | ||
export interface HigherOrderAnimation { | ||
isHigherOrder?: boolean; | ||
} | ||
|
||
export type AnimationCallback = ( | ||
finished?: boolean, | ||
current?: PrimitiveValue | ||
) => void; | ||
|
||
export type NextAnimation<T extends AnimationObject> = T | (() => T); | ||
|
||
export type SharedValue = { | ||
// TODO: just temporary mock | ||
value: unknown; | ||
}; | ||
|
||
export type Timestamp = number; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import { defineAnimation } from './util'; | ||
import { | ||
Animation, | ||
AnimationCallback, | ||
AnimationObject, | ||
PrimitiveValue, | ||
Timestamp, | ||
} from './commonTypes'; | ||
import { Platform } from 'react-native'; | ||
|
||
interface DecayConfig { | ||
deceleration?: number; | ||
velocityFactor?: number; | ||
clamp?: number[]; | ||
velocity?: number; | ||
} | ||
|
||
export interface DecayAnimation extends Animation<DecayAnimation> { | ||
lastTimestamp: Timestamp; | ||
startTimestamp: Timestamp; | ||
initialVelocity: number; | ||
velocity: number; | ||
current: PrimitiveValue; | ||
} | ||
|
||
export interface InnerDecayAnimation | ||
extends Omit<DecayAnimation, 'current'>, | ||
AnimationObject { | ||
current: number; | ||
} | ||
|
||
export function withDecay( | ||
userConfig: DecayConfig, | ||
callback?: AnimationCallback | ||
): Animation<DecayAnimation> { | ||
'worklet'; | ||
|
||
return defineAnimation<DecayAnimation>(0, () => { | ||
'worklet'; | ||
const config: Required<DecayConfig> = { | ||
deceleration: 0.998, | ||
velocityFactor: Platform.OS !== 'web' ? 1 : 1000, | ||
clamp: [], | ||
velocity: 0, | ||
}; | ||
if (userConfig) { | ||
Object.keys(userConfig).forEach( | ||
(key) => | ||
((config as any)[key] = userConfig[key as keyof typeof userConfig]) | ||
); | ||
} | ||
|
||
const VELOCITY_EPS = Platform.OS !== 'web' ? 1 : 1 / 20; | ||
const SLOPE_FACTOR = 0.1; | ||
|
||
function decay(animation: InnerDecayAnimation, now: number): boolean { | ||
const { | ||
lastTimestamp, | ||
startTimestamp, | ||
initialVelocity, | ||
current, | ||
velocity, | ||
} = animation; | ||
|
||
const deltaTime = Math.min(now - lastTimestamp, 64); | ||
const v = | ||
velocity * | ||
Math.exp( | ||
-(1 - config.deceleration) * (now - startTimestamp) * SLOPE_FACTOR | ||
); | ||
animation.current = | ||
current + (v * config.velocityFactor * deltaTime) / 1000; // /1000 because time is in ms not in s | ||
animation.velocity = v; | ||
animation.lastTimestamp = now; | ||
|
||
if (config.clamp) { | ||
if (initialVelocity < 0 && animation.current <= config.clamp[0]) { | ||
animation.current = config.clamp[0]; | ||
return true; | ||
} else if ( | ||
initialVelocity > 0 && | ||
animation.current >= config.clamp[1] | ||
) { | ||
animation.current = config.clamp[1]; | ||
return true; | ||
} | ||
} | ||
|
||
return Math.abs(v) < VELOCITY_EPS; | ||
} | ||
|
||
function validateConfig(): void { | ||
if (config.clamp) { | ||
if (!Array.isArray(config.clamp)) { | ||
throw Error( | ||
`config.clamp must be an array but is ${typeof config.clamp}` | ||
); | ||
} | ||
if (config.clamp.length !== 2) { | ||
throw Error( | ||
`clamp array must contain 2 items but is given ${config.clamp.length}` | ||
); | ||
} | ||
} | ||
if (config.velocityFactor <= 0) { | ||
throw Error( | ||
`config.velocityFactor must be greather then 0 but is ${config.velocityFactor}` | ||
); | ||
} | ||
} | ||
|
||
function onStart( | ||
animation: DecayAnimation, | ||
value: number, | ||
now: Timestamp | ||
): void { | ||
animation.current = value; | ||
animation.lastTimestamp = now; | ||
animation.startTimestamp = now; | ||
animation.initialVelocity = config.velocity; | ||
validateConfig(); | ||
} | ||
|
||
return { | ||
onFrame: decay, | ||
onStart, | ||
callback, | ||
velocity: config.velocity ?? 0, | ||
initialVelocity: 0, | ||
current: 0, | ||
lastTimestamp: 0, | ||
startTimestamp: 0, | ||
} as DecayAnimation; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { defineAnimation } from './util'; | ||
import { | ||
Animation, | ||
NextAnimation, | ||
Timestamp, | ||
HigherOrderAnimation, | ||
PrimitiveValue, | ||
} from './commonTypes'; | ||
|
||
export interface DelayAnimation | ||
extends Animation<DelayAnimation>, | ||
HigherOrderAnimation { | ||
startTime: Timestamp; | ||
started: boolean; | ||
previousAnimation: DelayAnimation | null; | ||
current: PrimitiveValue; | ||
} | ||
|
||
export function withDelay( | ||
delayMs: number, | ||
_nextAnimation: NextAnimation<DelayAnimation> | ||
): Animation<DelayAnimation> { | ||
'worklet'; | ||
return defineAnimation<DelayAnimation>(_nextAnimation, () => { | ||
'worklet'; | ||
const nextAnimation = | ||
typeof _nextAnimation === 'function' ? _nextAnimation() : _nextAnimation; | ||
|
||
function delay(animation: DelayAnimation, now: Timestamp): boolean { | ||
const { startTime, started, previousAnimation } = animation; | ||
|
||
if (now - startTime > delayMs) { | ||
if (!started) { | ||
nextAnimation.onStart( | ||
nextAnimation, | ||
animation.current, | ||
now, | ||
previousAnimation as DelayAnimation | ||
); | ||
animation.previousAnimation = null; | ||
animation.started = true; | ||
} | ||
const finished = nextAnimation.onFrame(nextAnimation, now); | ||
animation.current = nextAnimation.current; | ||
return finished; | ||
} else if (previousAnimation) { | ||
const finished = previousAnimation.onFrame(previousAnimation, now); | ||
animation.current = previousAnimation.current; | ||
if (finished) { | ||
animation.previousAnimation = null; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
function onStart( | ||
animation: DelayAnimation, | ||
value: PrimitiveValue, | ||
now: Timestamp, | ||
previousAnimation: DelayAnimation | ||
): void { | ||
animation.startTime = now; | ||
animation.started = false; | ||
animation.current = value; | ||
animation.previousAnimation = previousAnimation; | ||
} | ||
|
||
const callback = (finished?: boolean): void => { | ||
if (nextAnimation.callback) { | ||
nextAnimation.callback(finished); | ||
} | ||
}; | ||
|
||
return { | ||
isHigherOrder: true, | ||
onFrame: delay, | ||
onStart, | ||
current: nextAnimation.current, | ||
callback, | ||
previousAnimation: null, | ||
startTime: 0, | ||
started: false, | ||
}; | ||
}); | ||
} | ||
|
||
/** | ||
* @deprecated Kept for backward compatibility. Will be removed soon. | ||
*/ | ||
export function delay( | ||
delayMs: number, | ||
_nextAnimation: NextAnimation<DelayAnimation> | ||
): Animation<DelayAnimation> { | ||
'worklet'; | ||
console.warn('Method `delay` is deprecated. Please use `withDelay` instead'); | ||
return withDelay(delayMs, _nextAnimation); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
export { | ||
AnimationObject, | ||
Animation, | ||
HigherOrderAnimation, | ||
AnimationCallback, | ||
NextAnimation, | ||
SharedValue, | ||
Timestamp, | ||
} from './commonTypes'; | ||
export { cancelAnimation, defineAnimation } from './util'; | ||
export { withTiming } from './timing'; | ||
export { withSpring } from './spring'; | ||
export { withDecay } from './decay'; | ||
export { withDelay } from './delay'; | ||
export { withRepeat } from './repeat'; | ||
export { withSequence } from './sequence'; | ||
export { withStyleAnimation } from './styleAnimation'; |
Oops, something went wrong.