Kinetic Components

Animate a single React component or orchestrate animations among a collection of React components.
Use CSS animations or your favorite JS animation library!
npm install kinetic-components --save

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
JS
CSS
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
JS
CSS
hide

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
JS
CSS
hide

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
JS
CSS
hide

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
JS
CSS
hide

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
JS
CSS
hide
wobble

Look at me. I'm animated!