Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix keyframe types #6438

Merged
merged 13 commits into from
Sep 2, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,26 @@ function App() {
<summary>Type definitions</summary>

```typescript
type FirstFrame =
| {
0: KeyframeProps & { easing?: never };
from?: never;
}
| {
0?: never;
from: KeyframeProps & { easing?: never };
};

type LastFrame =
| { 100?: KeyframeProps; to?: never }
| { 100?: never; to: KeyframeProps };

export type ValidKeyframeProps = FirstFrame &
LastFrame &
Record<number, KeyframeProps>;

class Keyframe {
constructor(definitions: Record<string, KeyframeProps>);
constructor(definitions: ValidKeyframeProps;
duration(durationMs: number): Keyframe;
delay(delayMs: number): Keyframe;
reduceMotion(reduceMotionV: ReduceMotion): Keyframe;
Expand All @@ -53,7 +71,7 @@ class Keyframe {

An object, that contains definitions of your animation.
The object keys should be within range `0-100` and correspond to animation progress.
The object values should consist of style props and/or [easing function](/docs/animations/withTiming/#easing). If easing property is not provided, it defaults to `Easing.linear`.
The object values should consist of style props and optionally of an [easing function](/docs/animations/withTiming/#easing). If easing property is not provided, it defaults to `Easing.linear`.

The keys take the following values:

Expand Down Expand Up @@ -84,6 +102,7 @@ keyframe

- Providing keyframe `0` or `from` is required as it contains the initial state of the object you want to animate.
- Ensure you provide the initial value for all style properties you want to animate in other keyframes.
- If you want to add easing to an animation between two keyframes you have to pass it to the second one. As a result you should never provide any easing to keyframe `0`.
- Do not provide both `0` and `from`, or `100` and `to` keyframes, as it will result in a parsing conflict.
- If you want to animate transform style, make sure that all properties in the transformation array are in the same order in all keyframes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import type {
EntryExitAnimationFunction,
IEntryExitAnimationBuilder,
KeyframeProps,
MaybeInvalidKeyframeProps,
StylePropsWithArrayTransform,
ValidKeyframeProps,
} from './commonTypes';
import type {
StyleProps,
Expand All @@ -33,14 +35,14 @@ class InnerKeyframe implements IEntryExitAnimationBuilder {
delayV?: number;
reduceMotionV: ReduceMotion = ReduceMotion.System;
callbackV?: (finished: boolean) => void;
definitions: Record<string, KeyframeProps>;
definitions: MaybeInvalidKeyframeProps;

/*
Keyframe definition should be passed in the constructor as the map
which keys are between range 0 - 100 (%) and correspond to the point in the animation progress.
*/
constructor(definitions: Record<string, KeyframeProps>) {
this.definitions = definitions;
constructor(definitions: ValidKeyframeProps) {
this.definitions = definitions as MaybeInvalidKeyframeProps;
}

private parseDefinitions(): ParsedKeyframesDefinition {
Expand Down Expand Up @@ -71,8 +73,8 @@ class InnerKeyframe implements IEntryExitAnimationBuilder {
delete this.definitions.to;
}
/*
One of the assumptions is that keyframe 0 is required to properly set initial values.
Every other keyframe should contain properties from the set provided as initial values.
One of the assumptions is that keyframe 0 is required to properly set initial values.
Every other keyframe should contain properties from the set provided as initial values.
*/
if (!this.definitions['0']) {
throw new Error(
Expand All @@ -99,9 +101,9 @@ class InnerKeyframe implements IEntryExitAnimationBuilder {
});

const duration: number = this.durationV ? this.durationV : 500;
const animationKeyPoints: Array<string> = Array.from(
const animationKeyPoints: Array<number> = Array.from(
Object.keys(this.definitions)
);
).map(parseInt);

const getAnimationDuration = (
key: string,
Expand Down Expand Up @@ -146,10 +148,10 @@ class InnerKeyframe implements IEntryExitAnimationBuilder {
});
};
animationKeyPoints
.filter((value: string) => parseInt(value) !== 0)
.sort((a: string, b: string) => parseInt(a) - parseInt(b))
.forEach((keyPoint: string) => {
if (parseInt(keyPoint) < 0 || parseInt(keyPoint) > 100) {
.filter((value: number) => value !== 0)
.sort((a: number, b: number) => a - b)
.forEach((keyPoint: number) => {
if (keyPoint < 0 || keyPoint > 100) {
throw new Error(
'[Reanimated] Keyframe should be in between range 0 - 100.'
);
Expand All @@ -161,7 +163,7 @@ class InnerKeyframe implements IEntryExitAnimationBuilder {
addKeyPoint({
key,
value,
currentKeyPoint: parseInt(keyPoint),
currentKeyPoint: keyPoint,
easing,
});
Object.keys(keyframe).forEach((key: string) => {
Expand Down Expand Up @@ -302,14 +304,12 @@ function makeKeyframeKey(index: number, transformProp: string) {
return `${index}_transform:${transformProp}`;
}

// TODO TYPESCRIPT This is a temporary type to get rid of .d.ts file.
export declare class ReanimatedKeyframe {
constructor(definitions: Record<string, KeyframeProps>);
constructor(definitions: ValidKeyframeProps);
duration(durationMs: number): ReanimatedKeyframe;
delay(delayMs: number): ReanimatedKeyframe;
reduceMotion(reduceMotionV: ReduceMotion): ReanimatedKeyframe;
withCallback(callback: (finished: boolean) => void): ReanimatedKeyframe;
}

// TODO TYPESCRIPT This temporary cast is to get rid of .d.ts file.
export const Keyframe = InnerKeyframe as unknown as typeof ReanimatedKeyframe;
export const Keyframe = InnerKeyframe as typeof ReanimatedKeyframe;
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ export interface KeyframeProps extends StyleProps {
easing?: EasingFunction;
}

type FirstFrame =
| {
0: KeyframeProps & { easing?: never };
from?: never;
}
| {
0?: never;
from: KeyframeProps & { easing?: never };
};

type LastFrame =
| { 100?: KeyframeProps; to?: never }
| { 100?: never; to: KeyframeProps };

export type ValidKeyframeProps = FirstFrame &
LastFrame &
Record<number, KeyframeProps>;
Latropos marked this conversation as resolved.
Show resolved Hide resolved

export type MaybeInvalidKeyframeProps = Record<number, KeyframeProps> & {
to?: KeyframeProps;
from?: KeyframeProps;
};

export type LayoutAnimation = {
initialValues: StyleProps;
animations: StyleProps;
Expand Down