Use CSS or JS animations
Animations are controlled with the `when` prop. This props takes an array of `predicate` - `animation function` pairs. The predicate determines if the animation function will run for the given `visible` prop.
The animation function has the signature:
(ctx: {node: HTMLElement}) => string | string[] | {finished: Promise}
To use CSS animations return the className(s) you want appended to the
`Animatable` component's element.
To use JS animations pass the HTML element into your animation library of choice and return a promise that resolves when the animation is finished.
import {predicates, Animate, AnimationCtx, AnimationResult} from 'kinetic-components';
import anime from 'animejs';
const animateIn = (): AnimationResult => ['animate__animated', 'animate__fadeInRight'];
const animateOut = (ctx: AnimationCtx): AnimationResult =>
anime({
targets: `#${ctx.node.id}`,
translateX: ['50%', 0],
opacity: [1, 0],
easing: 'linear',
duration: 200
});
export default ({isVisible, animateIn, animateOut, styles}) => (
<Animate
visible={isVisible}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={styles}>
<h4>Look at me. I'm animated!</h4>
</Animatable>
</Animate>
);
Single component
Animate a single component using the `Animate` and `Animatable` components. The `Animate` component controls the `Animatable` component.
import {Animate, Animatable, predicates} from 'kinetic-components';
export default ({isVisible, animateIn, animateOut, styles}) => (
<Animate
visible={isVisible}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={styles}>
<h4>Look at me. I'm animated!</h4>
</Animatable>
</Animate>
);
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
hide
Look at me. I'm animated!
Parent and child:
with no orchestration binding
There is no orchestration binding between the two components. This means the parent can leave at any time and the child can enter at any time.
import {Animate, Animatable, predicates} from 'kinetic-components';
export default ({isVisibleParent, isVisibleChild, animateIn, animateOut, parentStyles, childStyles}) => (
<Animate
visible={isVisibleParent}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={parentStyles}>
<Animate
visible={isVisibleChild}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={childStyles} />
</Animate>
</Animatable>
</Animate>
});
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
Parent and child:
child waits for parent to enter
Uses props: - animationBinding
- enterAfterParentFinish
Using `animationBindings`, an `Animatable` can be bound to parent and child animation contexts.
import {Animate, Animatable, predicates} from 'kinetic-components';
export default ({isVisibleParent, isVisibleChild, animateIn, animateOut, parentStyles, childStyles}) => (
<Animate
visible={isVisibleParent}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={parentStyles}>
{animationBinding => (
<Animate
visible={isVisibleChild}
enterAfterParentFinish
animationBinding={animationBinding}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={childStyles}/>
</Animate>
)}
</Animatable>
</Animate>
});
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
Parent and child:
parent waits for child to exit
Uses props: - animationBinding
- exitAfterChildFinish
import {Animate, Animatable, predicates} from 'kinetic-components';
export default ({isVisibleParent, isVisibleChild, animateIn, animateOut, parentStyles, childStyles}) => (
<Animate
visible={isVisibleOne}
exitAfterChildFinish={['child']}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={parentStyles}>
{animationBinding => (
<Animate
id={'child'}
visible={isVisibleTwo}
animationBinding={animationBinding}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={childStyles}/>
</Animate>
)}
</Animatable>
</Animate>
});
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
Children of children:
everyone waits for everyone else
Uses props: - animationBinding
- exitAfterChildFinish
- enterAfterParentFinish
import {Animate, Animatable, predicates} from 'kinetic-components';
export default ({isVisibleParent, isVisibleChildOne, isVisibleChildTwo, animateIn, animateOut, parentStyles, childOneStyles, childTwoStyles}) => (
<Animate
visible={isVisibleParent}
exitAfterChildFinish={['child-one']}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={parentStyles}>
{parentAnimationBinding => (
<Animate
id={'child-one'}
visible={isVisibleChildOne}
exitAfterChildFinish={['child-two']}
enterAfterParentFinish
animationBinding={parentAnimationBinding}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={childOneStyles}>
{childAnimationBinding => (
<Animate
id={'child-two'}
visible={isVisibleChildTwo}
enterAfterParentFinish
animationBinding={childAnimationBinding}
when={[
[predicates.isVisible, animateIn],
[predicates.isHidden, animateOut]
]}
>
<Animatable styles={childTwoStyles} />
</Animate>
)}
</Animatable>
</Animate>
)}
</Animatable>
</Animate>
)};
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
Custom predicates and trigger states
The `Animate` component will run whenever the required `visible` prop or optional `triggerState` prop changes. When the `Animate` component runs, it iterates over the predicate - animationFn pairings in the `when` prop, stopping at the first pairing whose predicate returns `true` and then calling the associated animationFn.
The `Animate` component also takes an optional `predicateState` prop. This prop holds state that is passed into the predicate when called.
The predicate function has the signature:
(predicateState: any, {triggerState, visible}: {triggerState: any; visible: boolean}) => boolean
A shallow diff is done on the `triggerState` with each change to determine if the Animate component should run. By coupling custom trigger states with custom predicates you can make animations that go beyond the normal show and hide animations. For example, you can add micro wobble effects when some scene data changes.
import {predicates, Animate, AnimationCtx, AnimationResult, Predicate} from 'kinetic-components';
import anime from 'animejs';
const shouldWobble: Predicate = (_, {prevTriggerState, triggerState, visible}) => {
return (visible && triggerState.wobbleState !== prevTriggerState.wobbleState) || false;
};
export default ({isVisible, wobbleState, shouldBounce, animateBounce, styles}) => (
<Animate
visible={isVisible}
triggerState={{wobbleState}}
when={[
[[predicates.wasPreviouslyVisible, predicates.isVisible, shouldWobble], animateBounce, {key: 'wobble'}]
[predicates.isHidden, animateOut]
[predicates.isVisible, animateIn],
]}
>
<Animatable styles={styles}>
<h4>Look at me. I can show, hide, and wobble!</h4>
</Animatable>
</Animate>
);
Click the 'show' and 'hide' buttons to run the animation. Toggle 'js' and 'css' to change
(behind the scenes) how the animation is executed via CSS or JS animate[In|Out] functions
hide
wobble
Look at me. I'm animated!