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

[Web LA] Add CurvedTransition #6239

Merged
merged 28 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4b8aaba
Works
m-bert Jul 9, 2024
ddb6e4f
Hide children
m-bert Jul 9, 2024
8a0db4e
Change easing type in TransitionData interface
m-bert Jul 9, 2024
cced080
Fix easing type
m-bert Jul 9, 2024
d2fb22a
Remove unused imports
m-bert Jul 9, 2024
6c4fabc
Reenable scale transition
m-bert Jul 9, 2024
08c57d3
Refactor createAnimation
m-bert Jul 9, 2024
21fe16b
Move type declaration
m-bert Jul 9, 2024
81250e5
componentUtils refactor
m-bert Jul 9, 2024
57f47e1
Change clone to dummy
m-bert Jul 9, 2024
378f9c6
Add cancel callback
m-bert Jul 9, 2024
d7bdb81
Add type to import
m-bert Jul 9, 2024
4c24b37
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 9, 2024
1b3c9d7
Add case block
m-bert Jul 9, 2024
1fdc1d6
Add use strict
m-bert Jul 9, 2024
af3f59c
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 10, 2024
93789d3
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 11, 2024
8610546
Mergele main
m-bert Jul 16, 2024
f4e9a3b
Make easing non-nullable
m-bert Jul 17, 2024
667d27c
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 17, 2024
e5048b7
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 17, 2024
3f4d815
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 18, 2024
9d2aef8
Move handleLayoutTransition
m-bert Jul 18, 2024
405be02
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 18, 2024
db1a273
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 18, 2024
3e762f0
Merge branch 'main' into @mbert/add-curved-transition
m-bert Jul 19, 2024
08df96e
Mergele main
m-bert Jul 22, 2024
52bcea2
Fix easing
m-bert Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export const WebEasings = {
exp: [0.7, 0, 0.84, 0],
};

export function getEasingByName(easingName: WebEasingsNames) {
return `cubic-bezier(${WebEasings[easingName].toString()})`;
}

export type WebEasingsNames = keyof typeof WebEasings;
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export interface TransitionData {
scaleX: number;
scaleY: number;
reversed?: boolean;
easingX?: string;
easingY?: string;
entering?: any;
exiting?: any;
}
Expand All @@ -51,9 +53,13 @@ export function convertAnimationObjectToKeyframes(

for (const [property, values] of Object.entries(style)) {
if (property === 'easing') {
const easingName = (
values.name in WebEasings ? values.name : 'linear'
) as WebEasingsNames;
let easingName: WebEasingsNames = 'linear';

if (values in WebEasings) {
easingName = values;
} else if (values.name in WebEasings) {
easingName = values.name;
}

keyframe += `animation-timing-function: cubic-bezier(${WebEasings[
easingName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { areDOMRectsEqual } from './domUtils';
import type { TransitionData } from './animationParser';
import { Keyframe } from '../animationBuilder';
import { makeElementVisible } from './componentStyle';
import { EasingNameSymbol } from '../../Easing';

function chooseConfig<ComponentProps extends Record<string, unknown>>(
animationType: LayoutAnimationType,
Expand Down Expand Up @@ -236,6 +237,10 @@ export function tryActivateLayoutTransition<
scaleX: snapshot.width / rect.width,
scaleY: snapshot.height / rect.height,
reversed: false, // This field is used only in `SequencedTransition`, so by default it will be false
easingX:
(props.layout as CustomConfig).easingXV?.[EasingNameSymbol] ?? 'ease',
easingY:
(props.layout as CustomConfig).easingYV?.[EasingNameSymbol] ?? 'ease',
entering: enteringAnimation,
exiting: exitingAnimation,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
CustomConfig,
KeyframeDefinitions,
} from './config';
import { WebEasings } from './Easing.web';
import { WebEasings, getEasingByName } from './Easing.web';
import type { WebEasingsNames } from './Easing.web';
import type { TransitionData } from './animationParser';
import { TransitionGenerator } from './createAnimation';
Expand All @@ -21,11 +21,12 @@ import type { ReanimatedSnapshot, ScrollOffsets } from './componentStyle';
import { setElementPosition, snapshots } from './componentStyle';
import { Keyframe } from '../animationBuilder';
import { ReducedMotionManager } from '../../ReducedMotion';
import { prepareCurvedTransition } from './transition/Curved.web';
import { EasingNameSymbol } from '../../Easing';

function getEasingFromConfig(config: CustomConfig): string {
if (!config.easingV) {
return `cubic-bezier(${WebEasings.linear.toString()})`;
return getEasingByName('linear');
}

const easingName = config.easingV[EasingNameSymbol];
Expand All @@ -35,12 +36,10 @@ function getEasingFromConfig(config: CustomConfig): string {
`[Reanimated] Selected easing is not currently supported on web.`
);

return `cubic-bezier(${WebEasings.linear.toString()})`;
return getEasingByName('linear');
}

return `cubic-bezier(${WebEasings[
easingName as WebEasingsNames
].toString()})`;
return getEasingByName(easingName as WebEasingsNames);
}

function getRandomDelay(maxDelay = 1000) {
Expand Down Expand Up @@ -232,6 +231,9 @@ export function handleLayoutTransition(
case 'JumpingTransition':
animationType = TransitionType.JUMPING;
break;
case 'CurvedTransition':
animationType = TransitionType.CURVED;
break;
case 'EntryExitTransition':
animationType = TransitionType.ENTRY_EXIT;
break;
Expand All @@ -240,11 +242,21 @@ export function handleLayoutTransition(
break;
}

animationConfig.animationName = TransitionGenerator(
animationType,
transitionData
);
const { transitionKeyframeName, dummyTransitionKeyframeName } =
TransitionGenerator(animationType, transitionData);

animationConfig.animationName = transitionKeyframeName;

if (animationType === TransitionType.CURVED) {
const { dummy, dummyAnimationConfig } = prepareCurvedTransition(
element,
animationConfig,
transitionData,
dummyTransitionKeyframeName! // In `CurvedTransition` it cannot be undefined
);

setElementAnimation(dummy, dummyAnimationConfig);
}
setElementAnimation(element, animationConfig);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ interface EasingType {

export interface CustomConfig {
easingV?: EasingType;
easingXV?: EasingType;
easingYV?: EasingType;
durationV?: number;
delayV?: number;
randomizeDelay?: boolean;
Expand All @@ -81,6 +83,7 @@ export enum TransitionType {
SEQUENCED,
FADING,
JUMPING,
CURVED,
ENTRY_EXIT,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SequencedTransition } from './transition/Sequenced.web';
import { FadingTransition } from './transition/Fading.web';
import { JumpingTransition } from './transition/Jumping.web';
import { insertWebAnimation } from './domUtils';
import { CurvedTransition } from './transition/Curved.web';
import { EntryExitTransition } from './transition/EntryExit.web';

type TransformType = NonNullable<TransformsStyle['transform']>;
Expand Down Expand Up @@ -156,6 +157,8 @@ export function TransitionGenerator(
transitionData: TransitionData
) {
const transitionKeyframeName = generateNextCustomKeyframeName();
let dummyTransitionKeyframeName;

let transitionObject;

switch (transitionType) {
Expand Down Expand Up @@ -183,6 +186,26 @@ export function TransitionGenerator(
transitionData
);
break;

// Here code block with {} is necessary because of eslint
case TransitionType.CURVED: {
dummyTransitionKeyframeName = generateNextCustomKeyframeName();

const { firstKeyframeObj, secondKeyframeObj } = CurvedTransition(
transitionKeyframeName,
dummyTransitionKeyframeName,
transitionData
);

transitionObject = firstKeyframeObj;

const dummyKeyframe =
convertAnimationObjectToKeyframes(secondKeyframeObj);

insertWebAnimation(dummyTransitionKeyframeName, dummyKeyframe);

break;
}
case TransitionType.ENTRY_EXIT:
transitionObject = EntryExitTransition(
transitionKeyframeName,
Expand All @@ -196,5 +219,5 @@ export function TransitionGenerator(

insertWebAnimation(transitionKeyframeName, transitionKeyframe);

return transitionKeyframeName;
return { transitionKeyframeName, dummyTransitionKeyframeName };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
'use strict';
import { LayoutAnimationType } from '../..';
import type { WebEasingsNames } from '../Easing.web';
import { getEasingByName } from '../Easing.web';
import type { TransitionData } from '../animationParser';
import type { AnimationConfig } from '../config';

function resetStyle(component: HTMLElement) {
component.style.animationName = ''; // This line prevents unwanted entering animation
component.style.position = 'absolute';
component.style.top = '0px';
component.style.left = '0px';
component.style.margin = '0px';
component.style.width = '100%';
component.style.height = '100%';
}

function showChildren(
parent: HTMLElement,
childrenDisplayProperty: Map<HTMLElement, string>,
shouldShow: boolean
) {
for (let i = 0; i < parent.children.length; ++i) {
const child = parent.children[i] as HTMLElement;

if (shouldShow) {
child.style.display = childrenDisplayProperty.get(child)!;
} else {
childrenDisplayProperty.set(child, child.style.display);
child.style.display = 'none';
}
}
}

function prepareParent(
element: HTMLElement,
dummy: HTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData
) {
// Adjust configs for `CurvedTransition` and create config object for dummy
animationConfig.easing = getEasingByName(
transitionData.easingX as WebEasingsNames
);

const childrenDisplayProperty = new Map<HTMLElement, string>();
showChildren(element, childrenDisplayProperty, false);

const originalBackgroundColor = element.style.backgroundColor;
element.style.backgroundColor = 'transparent';

const onFinalize = () => {
if (element.contains(dummy)) {
element.removeChild(dummy);
}

showChildren(element, childrenDisplayProperty, true);

element.style.backgroundColor = originalBackgroundColor;
};

const animationCancelCallback = () => {
onFinalize();
element.removeEventListener('animationcancel', animationCancelCallback);
};

const animationEndCallback = () => {
onFinalize();
element.removeEventListener('animationend', animationEndCallback);
};

element.addEventListener('animationend', animationEndCallback);
element.addEventListener('animationcancel', animationCancelCallback);

element.appendChild(dummy);
}

function prepareDummy(
element: HTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData,
dummyTransitionKeyframeName: string
) {
const dummyAnimationConfig: AnimationConfig = {
animationName: dummyTransitionKeyframeName,
animationType: LayoutAnimationType.LAYOUT,
duration: animationConfig.duration,
delay: animationConfig.delay,
easing: getEasingByName(transitionData.easingY as WebEasingsNames),
callback: null,
reversed: false,
};

const dummy = element.cloneNode(true) as HTMLElement;
resetStyle(dummy);

return { dummy, dummyAnimationConfig };
}

export function prepareCurvedTransition(
element: HTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData,
dummyTransitionKeyframeName: string
) {
const { dummy, dummyAnimationConfig } = prepareDummy(
element,
animationConfig,
transitionData,
dummyTransitionKeyframeName
);

prepareParent(element, dummy, animationConfig, transitionData);

return { dummy, dummyAnimationConfig };
}

export function CurvedTransition(
keyframeXName: string,
keyframeYName: string,
transitionData: TransitionData
) {
const keyframeXObj = {
name: keyframeXName,
style: {
0: {
transform: [
{
translateX: `${transitionData.translateX}px`,
scale: `${transitionData.scaleX},${transitionData.scaleY}`,
},
],
},
},
duration: 300,
};

const keyframeYObj = {
name: keyframeYName,
style: {
0: {
transform: [
{
translateY: `${transitionData.translateY}px`,
scale: `${transitionData.scaleX},${transitionData.scaleY}`,
},
],
},
},
duration: 300,
};

return {
firstKeyframeObj: keyframeXObj,
secondKeyframeObj: keyframeYObj,
};
}