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

react-tweenful

v0.1.0

Published

Animation engine for React

Downloads

31

Readme

react-tweenful

Looking for an amazing React library for animating stuff? Look no more, we've got you covered!

Demo

https://teodosii.github.io/react-tweenful/

Features

  • Loop support (infinite or up to a specific number)
  • Wide variety of easings (bezier, predefined and custom easing)
  • Delayed animations (before and after)
  • Events support
  • Negative delay support to mimic CSS animations
  • Percent based animations to mimic CSS animations (e.g. 0%, 50%, 100%)
  • Tweenful component for animating DOM nodes
  • SVG component to animate SVG nodes
  • Observer component for mount/unmount animations
  • ObserverGroup component to handle child transition (list removal/insertion, page transition etc)

Getting started

npm install react-tweenful

Development

In order to build the project and run it on your local machine, you'll need to build both the site project and the library itself. The site project will have a dependency of react-tweenful and there you'll be able to play with the examples.

npm run build:library:dev
npm run start

Or if you want to be able to modify both projects at once and let webpack watch the changes and do the build for you, then open 2 separate terminals and run the following commands

npm run watch:library:dev

And in a separate terminal run

npm run start

Both commands will instruct webpack to watch for changes. Navigate to http://localhost:8080/react-tweenful or whatever port you're running the application on.

Usage

react-tweenful exports the following:

  • Tweenful - component to animate DOM elements. It requires a DOM node to perform animation on.
  • SVG - component to animate SVG elements. It requires a SVG node to perform animation on.
  • Observer - component to animate mounting and unmounting of an element.
  • ObserverGroup - component to watch over a list of Observer elements such as list removal/insertion or route transition

A couple of utility functions are also exported to help you out animating:

  • percentage for percentage based animations
  • bezier for bezier easings
  • elastic for elastic easing

Import the needed component, for example Tweenful

import Tweenful, { elastic } from 'react-tweenful';

Tweenful requires a node to render on which it will perform the animation. We've got most of the DOM nodes covered in the form of namespacing such as Tweenful.div, Tweenful.span and so on.

const Example = () => (
  <Tweenful.div	
    className="tween-box"	
    duration={2000}	
    easing={elastic(1, 0.1)}
    style={{ position: 'relative' }}	
    animate={{ left: ['0px', '250px'] }}
  ></Tweenful.div>
);

Voila!

Examples

Observer

View animation here

Usage of Observer component together with Tweenful. Animate movement with Tweenful and subscribe to mount and unmount with Observer.

Observer

import React, { useEffect, useState } from 'react';
import Tweenful, { Observer, elastic } from 'react-tweenful';

const props = {
  delay: 200,
  render: true,
  duration: 1600,
  easing: elastic(1, 0.1),
  loop: false,
  animate: { translateX: '400px' },
  events: {
    onAnimationStart: () => console.log('AnimationStart'),
    onAnimationEnd: () => console.log('AnimationEnd')
  }
};

const ObserverDemo = () => {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => setShouldRender(false), 3000);
  }, []);

  return (
    <Observer
      render={shouldRender}
      duration={1200}
      style={{ opacity: 0 }}
      mount={{ opacity: 1 }}
      unmount={[{ opacity: 0 }]}
      easing="linear"
    >
      <div className="observer-demo">
        <Tweenful.div className="box-demo" {...props}></Tweenful.div>
      </div>
    </Observer>
  );
};

ObserverGroup

View animation here

Usage of ObserverGroup component to watch for mounting and unmounting over a list of notifications

Notifications

<ObserverGroup
  config={{
    duration: 800,
    style: { overflow: 'hidden' },
    mount: { opacity: [0, 1], height: ['0px', 'auto'] },
    unmount: { opacity: [1, 0], height: ['auto', '0px'] },
    easing: 'easeInOutCubic'
  }}
  skipInitial={true}
>
  {this.state.notifications.map(notification => (
    <Notification
      key={notification.id}
      notification={notification}
      onClick={this.removeNotification}
    />
  ))}
</ObserverGroup>

Animate route transition

View animation here

Usage of ObserverGroup to animate route change

Routing

import React from 'react';
import { Route, Switch, NavLink } from 'react-router-dom';
import ObserverGroup from 'react-tweenful/ObserverGroup';
import Observer from 'react-tweenful/Observer';

const colors = {
  red: '#EE4266',
  yellow: '#FDB833',
  blue: '#296EB4',
  green: '#0EAD69'
};

const Red = () => <div className="color-block" style={{ backgroundColor: colors.red }}></div>;
const Yellow = () => <div className="color-block" style={{ backgroundColor: colors.yellow }}></div>;
const Blue = () => <div className="color-block" style={{ backgroundColor: colors.blue }}></div>;
const Green = () => <div className="color-block" style={{ backgroundColor: colors.green }}></div>;

class RouteTransitionDemo extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const { location } = this.props;

    return (
      <div className="route-transition-demo">
        <ul className="nav-links">
          <li><NavLink to="/transition/red">Red</NavLink></li>
          <li><NavLink to="/transition/yellow">Yellow</NavLink></li>
          <li><NavLink to="/transition/blue">Blue</NavLink></li>
          <li><NavLink to="/transition/green">Green</NavLink></li>
        </ul>
        <div className="observer">
          <ObserverGroup>
            <Observer.div
              key={location.pathname}
              className="key-wrapper"
              duration={1000}
              style={{ opacity: 0 }}
              mount={{ opacity: 1 }}
              unmount={{ opacity: 0 }}
              easing={'easeOutQuad'}
            >
              <Switch location={location}>
                <Route exact path={`/transition/red`} component={Red} />
                <Route exact path={`/transition/green`} component={Green} />
                <Route exact path={`/transition/blue`} component={Blue} />
                <Route exact path={`/transition/yellow`} component={Yellow} />
                <Route render={() => <div>Not Found</div>} />
              </Switch>
            </Observer.div>
          </ObserverGroup>
        </div>
      </div>
    );
  }
}

export default RouteTransitionDemo;

Prism

View animation here

react-tweenful in action powering a super awesome looking animation.

Prism

import React from 'react';
import { SVG } from 'react-tweenful';

const WAVE_COUNT = 16;
const offset = 40;
const waveLength = 375;
const duration = 1500;

const waves = new Array(WAVE_COUNT).fill(0).map((wave, i) => ({
  key: i + 1,
  style: {
    transformOrigin: '500px 500px',
    opacity: 4 / WAVE_COUNT,
    mixBlendMode: 'screen',
    fill: `hsl(${(360 / WAVE_COUNT) * (i + 1)}, 100%, 50%)`,
    transform: `rotate(${(360 / WAVE_COUNT) * i}deg) translate(${waveLength}px, ${offset}px)`
  },
  rotate: `${(360 / WAVE_COUNT) * (i + 1)}deg`,
  translate: `${waveLength}px ${offset}px`,
  angle: `${(360 / WAVE_COUNT) * (i + 1)}deg`,
  delay: (duration / WAVE_COUNT) * (i + 1) * -3,
  path:
    'M-1000,1000V388c86-2,111-38,187-38s108,38,187,38,111-38,187-38,108,38,187,38,111-38,187-38,108,38,187,38,111-38,187-38,109,38,188,38,110-38,187-38,108,38,187,38,111-38,187-38,108,38,187,38,111-38,187-38,108,38,187,38,111-38,187-38,109,38,188,38c0,96,0,612,0,612Z'
}));

const RotatingSvg = () => {
  return (
    <svg height="300" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1000 1000">
      <defs>
        <clipPath id="world">
          <circle cx="500" cy="500" r="450" stroke="none" fill="none"></circle>
        </clipPath>
      </defs>
      <circle cx="500" cy="500" r="450" stroke="none" fill="#000"></circle>
      <SVG
        type="g"
        className="circle"
        loop={true}
        duration={(WAVE_COUNT / 2) * duration}
        style={{ transformOrigin: '500px 500px' }}
        easing="linear"
        animate={{ rotate: '360deg' }}
      >
        {waves.map(wave => (
          <SVG.path
            loop={true}
            id={wave.key}
            key={wave.key}
            easing="linear"
            duration={1500}
            d={wave.path}
            style={wave.style}
            delay={wave.delay}
            transform={{ rotate: wave.rotate, translate: wave.translate }}
            animate={{ rotate: `${wave.angle}`, translate: `0px ${offset}px` }}
          />
        ))}
      </SVG>
    </svg>
  );
};

SVG

View animation here

An example animating SVG path and fill properties one after the other.

SVG

Gradients

View animation here

An example using elastic easing to animate gradient boxes.

Gradients

import React from 'react';
import Tweenful, { elastic } from 'react-tweenful';

const Gradients = () => {
  const elements = new Array(10)
    .fill(0)
    .map((_e, i) => (
      <Tweenful.div
        className={`gradiant${i + 1} box`}
        loop={true}
        easing={elastic(1, 0.1)}
        duration={3000}
        delay={i * 100}
        endDelay={(10 - i) * 100}
        animate={[{ height: '20%' }, { height: ['20%', 'auto'] }]}
        key={i}
      ></Tweenful.div>
    ));

  return (
    <div className="gradients-container">
      <div className="row">{elements}</div>
    </div>
  );
};

Loader

View animation here

Loader

import React from 'react';
import Tweenful, { percentage } from 'react-tweenful';

const rotate = percentage({
  '0%': { translate: '-50% -50%', rotate: '0deg' },
  '50%': { translate: '-50% -50%', rotate: '0deg' },
  '80%': { translate: '-50% -50%', rotate: '360deg' },
  '100%': { translate: '-50% -50%', rotate: '360deg' }
});
const dot1Animate = percentage({
  '0%': { scale: 1 },
  '20%': { scale: 1 },
  '45%': { translate: '16px 12px', scale: 0.45 },
  '60%': { translate: '160px 150px', scale: 0.45 },
  '80%': { translate: '160px 150px', scale: 0.45 },
  '100%': { translate: '0px 0px', scale: 1 }
});
const dot2Animate = percentage({
  '0%': { scale: 1 },
  '20%': { scale: 1 },
  '45%': { translate: '-16px 12px', scale: 0.45 },
  '60%': { translate: '-160px 150px', scale: 0.45 },
  '80%': { translate: '-160px 150px', scale: 0.45 },
  '100%': { translate: '0px 0px', scale: 1 }
});
const dot3Animate = percentage({
  '0%': { scale: 1 },
  '20%': { scale: 1 },
  '45%': { translateY: '-18px', scale: 0.45 },
  '60%': { translateY: '-180px', scale: 0.45 },
  '80%': { translateY: '-180px', scale: 0.45 },
  '100%': { translateY: '0px', scale: 1 }
});

const LoadingCircles = () => {
  return (
    <div className="loading-wrapper">
      <Tweenful.div
        className="loading-circles-container"
        duration={2000}
        loop={true}
        easing="easeInOutCubic"
        transform={{ translate: '-50% -50%' }}
        animate={rotate}
      >
        <Tweenful.div
          className="dot dot-1"
          duration={2000}
          easing="easeOutCubic"
          loop={true}
          transform={{ translate: '0px 0px', scale: 1 }}
          animate={dot1Animate}
        ></Tweenful.div>
        <Tweenful.div
          className="dot dot-2"
          duration={2000}
          easing="easeOutCubic"
          loop={true}
          transform={{ translate: '0px 0px', scale: 1 }}
          animate={dot2Animate}
        ></Tweenful.div>
        <Tweenful.div
          className="dot dot-3"
          duration={2000}
          easing="easeOutCubic"
          loop={true}
          transform={{ translateY: '0px', scale: 1 }}
          animate={dot3Animate}
        ></Tweenful.div>
      </Tweenful.div>

      <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <defs>
          <filter id="goo">
            <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
            <feColorMatrix
              in="blur"
              mode="matrix"
              values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 21 -7"
            />
          </filter>
        </defs>
      </svg>
    </div>
  );
};

Bouncing Balls

View animation here

Bouncing balls illustrate negative delay support in react-tweenful

Bouncing Balls

import React from 'react';
import { SVG, percentage, elastic } from 'react-tweenful';

const circles = new Array(10).fill(0).map((_e, i) => ({
  loop: true,
  fill: `hsl(${(i + 1) * 20 - 20}, 70%, 70%)`,
  delay: ((i + 1) * 1500) / -10,
  duration: 1500,
  easing: elastic(2, 0.9),
  transform: {
    translate: '0 100px'
  },
  style: {
    transformOrigin: `${-200 + 120 * (i + 1)}px 250px`
  },
  animate: percentage({
    '0%': { translate: '0px 100px', scale: 1 },
    '50%': { translate: '0px -100px', scale: 0.3 },
    '100%': { translate: '0px 100px', scale: 1 }
  }),
  r: 35,
  cx: 100 * i + 50,
  cy: 250
}));

const BouncingBalls = () => {
  return (
    <div className="bouncing-balls">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1000 500">
        {circles.map((circle, i) => (
          <SVG.circle key={i} {...circle}></SVG.circle>
        ))}
      </svg>
    </div>
  );
};

API

Update in progress. Stay in touch!

Note

A couple of the animations shown here have been initially created for CodePen by other folks. My only contribution to them was to convert them to react-tweenful to show real world examples on animating stuff.

License

MIT