@huds0n/animations
TypeScript icon, indicating that this package has built-in type declarations

1.6.0-beta63 • Public • Published

@huds0n/animations

Status GitHub Issues GitHub Pull Requests License


A collection of animated components, including Animated View, Animated Text, Animated Lists, Transition Containers, and more...

📝 Table of Contents

🧐 About

A simple and declarative way to provide complex, interactive animations for React Native.

<AnimatedText
  style={{
    color: "orange",
    fontSize: 48,
  }}
  animate={[
    {
      to: { transform: [{ scale: 1 }] },
      type: "SPRING",
      bounciness: 10,
      speed: 1,
    },
    {
      to: { color: "red" },
      duration: 150,
      loop: true,
    },
  ]}
>
  Welcome
</AnimatedText>

List of Features

  • Simple: No need to deal with Animated.Values to make great animations.
  • Declarative: Describe your animations rather than build them.
  • Powerful: Run multiple independent animations at once.
  • Serializable: Schedule animations to follow one another.
  • Smooth: Use useNativeDriver for extra performance.
  • Type-Safe: Fully integrated with typescript out-of-the-box.

🏁 Getting Started

Prerequisites

Works with any project implementing React 16.8 onwards

Installing

npm i @huds0n/animations

🧑‍💻 Basic Usage

Base Style

Animated components style identically to their static counterpart. Updating style will immediately change the appearance of a component.

import { AnimatedView } from "@huds0n/animations";

// Inside component

<AnimatedView
  style={{
    height: 50,
    width: 100,
    backgroundColor: isSelected ? "red" : "blue",
  }}
/>;

Basic Animation

Passing an animationProps into animate will cause the component to transition to the new style. Animated Components will automatically detect and start animations on animate change.

<AnimatedView
  style={{
    backgroundColor: "blue",
    width: 100,
  }}
  animate={{
    to: {
      backgroundColor: "red",
      height: 50,
    },
    delay: 500,
    duration: 1000,
  }}
/>

Note that animations will intelligently calculate style property start values even if it is already in mid animation! However, it's important not to mix numbered dimensions with string dimensions. If no value is specified it assumes 0, '0%', '0deg', and 'transparent' for numbers, percentage dimensions, rotations, and colors respectively.

Looping

Loop can be added to an animationProps and can be easily controlled in three ways:

  • boolean - permanent looping;
  • number (multiples of 0.5) - select number of loops before stopping;
  • ref ({ current: boolean }) - detect and stops looping once set to false, e.g loopRef.current = false.
// Loop ref
const loopRef = useRef(true)

// Component
<AnimatedView
  style={{
    backgroundColor: 'blue',
    width: 100,
  }}
  animate={{
    to: {
      backgroundColor: 'red',
      height: 50,
    },
    delay: 500,
    duration: 1000,
    loop: loopRef,
  }}
/>

Parallel Animations

Multiple animations can be started at once on a component. To do this simply pass the animations in as an array.

<AnimatedView
  style={{
    backgroundColor: "blue",
    width: 100,
  }}
  animate={[
    {
      to: { height: 50 },
      delay: 500,
      duration: 1000,
    },
    {
      to: { backgroundColor: "red" },
      duration: 250,
      loop: true,
    },
  ]}
/>

Sequence Animations

To stack animations pass the following animationProps as a return value in the function onAnimationEnd.

<AnimatedView
  style={{
    height: 50,
    width: 100,
    backgroundColor: "blue",
  }}
  animate={{
    to: { height: 50 },
    delay: 500,
    duration: 1000,
    onAnimationEnd: () => ({
      to: { backgroundColor: "red" },
      duration: 250,
      loop: true,
    }),
  }}
/>

🧑‍🔬 Advanced Usage

Create Animated Component

Any component which has a style prop can be turned into an animated component using createAnimatedComponent.

import { Image } from "react-native";
import { createAnimatedComponent } from "@huds0n/animations";

const AnimatedImage = createAnimatedComponent(Image);

Use Native Driver

For extra performance pass in the useNativeDriver prop. However, this can only work if you are animating native driver compatible props, such as opacity and transforms.

<AnimatedView
  style={{
    backgroundColor: "blue",
    height: 100,
    width: 100,
  }}
  animate={{
    opacity: 1,
  }}
  useNativeDriver
/>

Attach Animations

Sometimes you may want to attach an animation to another Animated.Value, e.g. a touch event created by PanResponder. By using the attach prop you set styles at specific Animated.Value input points. The component will then animate fluidly between the input points.

<AnimatedView
  {...panResponder.panHandlers}
  attach={{
    at: [
      {
        input: -200,
        style: { backgroundColor: "blue", transform: [{ translateY: -200 }] },
      },
      {
        input: 0,
        style: { backgroundColor: "black" },
      },
      {
        input: 200,
        style: { backgroundColor: "red", transform: [{ translateY: 200 }] },
      },
    ],
    animatedValue: panY,
  }}
/>

For more complex animation paths you can set a style function to run over a set range of inputs. The smoothness of the path will depend on the number of points you chose but will require increasing calculations.

<AnimatedView
  attach={{
    over: {
      inputStart: -200,
      inputEnd: 200,
      points: 20,
      fn: (input) => ({
        transform: [
          { translateY: input },
          {
            translateX: Math.pow(input / 200, 3) * 80,
          },
        ],
      }),
    },
    animatedValue: panY,
  }}
/>

See example for use case with PanResponder.


📖 Reference

Animation Props

Prop Required/Default Description Type
to ✔️ Style to animate to textStyle or viewStyle
loop false See looping boolean, number, or number ref
onAnimationEnd - Called when animation endsPasses array of style props attached to animationAny animationProp returned is ran in sequence (attachedProps: string array) => void or animationProps
onAnimationStart - Called when animation starts () => void
type 'TIMING' Type of animation method 'TIMING', 'SPRING', or 'DECAY'

All Animation method specific props can also be used, such as duration, velocity, and friction.

🎁 Extras

ColorFader

Container extending React Native's View that smoothly animates background color.

<ColorFader backgroundColor={isSelected ? "red" : "blue"}>
  // View's children
</ColorFader>

Additional props:

Prop Required/Default Description Type
animate true Toggles color animation boolean
animation { type: 'TIMING', duration: 100} Color animation config AnimationProps with 'to'
backgroundColor null Background color string or null
overrideColor - Overrides backgroundColor without animation string

ContentsFader

Container extending React Native's View that fades between it's child components.

<ContentsFader dependencies={[isSelected]}>
  {isSelected && <SelectedComponent />}
  {!isSelected && <UnSelectedComponent />}
</ContentsFader>

Additional props:

Prop Required/Default Description Type
animate true Toggles fade animation boolean
animationDuration 500 Duration of cross fade number
dependencies ✔️ Array of variables which on change invoke fade array of any
easing - Animation easing (value: number) => number
fadeOverlap 1/3 Ratio of cross fade overlapping number
useNativeDriver true Whether animation uses native driver boolean

Transition

Combines ColorFader and ContentsFader with automatic resizing based on content size.

<TransitionContainer
  backgroundColor={currentButton.backgroundColor}
  style={{
    borderWidth: StyleSheet.hairlineWidth,
    height: 40,
    padding: 10,
    borderRadius: 10,
  }}
  dependencies={[currentButton.text]}
>
  <Text style={{ textAlign: "center" }}>{currentButton.text}</Text>
</TransitionContainer>

Lists

Adds animation attached to flatlist scroll with automatic animate in.

Additional props:

Prop Required/Default Description Type
animationDelay 0 Delay in ms on animating in number
animationDuration 2500 Duration in ms of animating in number
at - Set styles at position points to animate between (ElementPosition) => attachAtProp
itemLength ✔️ Length of item element in direction of scroll number
footerOffset - Footer offset number
headerOffset - Header offset number
ListComponent FlatList List component to animate (will inherit props) React List Component
offsetAnim - Scroll animated valueCan be attached to other Animated Components for complex animations Animated Value
onAnimationEnd - Called when animation in complete () => void
over - Set style function animate over (ElementPosition) => attachOverProp
renderItem ✔️ Item render functionPassed ElementPosition with ListRenderItemInfo (ElementPosition & ListRenderItemInfo) => React Component
useNativeDriver false Whether animation uses native driver boolean

ElementPosition:

Prop Description Type
index Item index number
row Item row number
start Scroll offset at which the element is after the start of the view number
end Scroll offset at which the element is after the end of the view number
<AnimatedList
  animationDuration={4000}
  itemLength={150}
  at={({ index, start, end }) => [
    /* At 150px above the top of the view items have an opacity 0.5 */
    { input: start + 150, style: { opacity: 0.5 } },

    /* When the item is in full view they have opacity 1 */
    { input: start, style: { opacity: 1 } },

    /* At 200px above the bottom of the view the items will start to fade from opacity 1 */
    {
      input: end + 200,
      style: { opacity: 1 },
    },

    /* At 100px above the bottom of the view the elements start their complex movement out */
    {
      input: end + 100,
      style: {
        transform: [
          { translateX: 0 },
          { translateY: 0 },
          { scale: 1 },
          { rotate: "0deg" },
        ],
      },
    },

    /* By the time the element reaches the bottom of the screen has completed it's complex movement.
    Note how remainder is use to calculate column */
    {
      input: end,
      style: {
        opacity: 0,
        transform: [
          { translateX: index % 2 ? 150 : -150 },
          { translateY: -150 },
          { scale: 0 },
          { rotate: index % 2 ? "-360deg" : "360deg" },
        ],
      },
    },
  ]}
  data={DEMO_DATA}
  keyName="value"
  numColumns={2}
  renderItem={({ item }) => {
    return (
      <View style={styles.itemContainer}>
        <Image source={{ uri: item.PICTURE_URI }} style={styles.itemPicture} />
      </View>
    );
  }}
  useNativeDriver
  reverseZIndex
  style={styles.flatlist}
/>

📲 Example

Clone or fork the repo at https://github.com/JontiHudson/modules-huds0n-animations

Go to the __example__ folder. Run npm install to install the expo project, then expo start to launch the example.

✍️ Authors

See also the list of contributors who participated in this project.

🎉 Acknowledgements

  • Special thanks to my fiance, Arma, who has been so patient with all my extra-curricular work.

Readme

Keywords

Package Sidebar

Install

npm i @huds0n/animations

Weekly Downloads

2

Version

1.6.0-beta63

License

MIT

Unpacked Size

2.87 MB

Total Files

76

Last publish

Collaborators

  • jonti_hudson