npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@huds0n/animations

v1.6.0-beta63

Published

Animates View, FlatList, and LazyList

Downloads

28

Readme

Status GitHub Issues GitHub Pull Requests License


📝 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.