Hello, everyone,
I'm very glad to have released an opensource project I've been working on lately - react-tweenful. Hence I'm sharing this with you and looking for your feedback!
Repository
https://github.com/teodosii/react-tweenful
Demo
https://teodosii.github.io/react-tweenful/
What is react-tweenful?
react-tweenful it's an animation engine written from scratch to help you animate stuff easily. Initially I wanted a replacement for react-transition-group but with an animation engine behind, hence I've written the engine and then written individual components to handle multiple cases - Tweenful, SVG, Observer and ObserverGroup. It's inspired by anime.js.
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%) -
Tweenfulcomponent for animating DOM nodes -
SVGcomponent to animate SVG nodes -
Observercomponent for mount/unmount animations -
ObserverGroupcomponent to handle child transition (list removal/insertion, page transition etc)
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 ofObserverelements such as list removal/insertion or route transition
A couple of utility functions are also exported to help you out animating:
-
percentagefor percentage based animations -
bezierfor bezier easings -
elasticfor 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>
);
Real world examples
Animate page route transition
react-tweenful can be used to animate children removal/insertion of a list or to animate betweeen routes with the help of Observer and ObserverGroup
<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>
Next example shows how easily we can animate when route changes. Watch the URL getting changed and see how the content is animated using a fade-in fade-out animation between previous and current page.
Prism
This example shows usage of negative delay, which was implemented into the library to mimic CSS animations.
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
The following shows usage of SVG component to animate path from start to end and then fill the form.
const SvgDemo = () => {
return (
<svg
height="300"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 400 400"
>
<SVG.path
duration={3000}
easing="easeInQuad"
style={{ fill: "#fff", transform: 'scale(4)' }}
animate={[{ strokeDashoffset: [100, 0] }, { fill: '#b91e1e' }]}
stroke="#b91e1e"
strokeWidth="2"
fill="none"
d="M61.9,55.4c-2.3-3.5-3.6-7.7-3.6-12.2c0-4.7,1.5-9.1,4-12.7c2.1,3.1,3.4,6.7,3.7,10.7h13 C78.2,24,65,10.1,48.1,8.2l-3.8-6.6l-3.8,6.6C23.5,10.1,10.3,24,9.5,41.3h13c0.3-3.9,1.6-7.6,3.7-10.7c2.5,3.6,4,8,4,12.7 c0,4.5-1.4,8.7-3.7,12.2c-2.3-3.2-3.8-7-4-11.2h-13c0.8,18.5,16,33.3,34.7,33.3S78.2,62.7,79,44.3h-13 C65.7,48.4,64.2,52.2,61.9,55.4z M36,62.9c3.9-5.6,6.2-12.3,6.2-19.6c0-7.6-2.5-14.7-6.8-20.4c2.7-1.2,5.6-1.9,8.8-1.9 c3.1,0,6.1,0.7,8.8,1.9c-4.2,5.7-6.8,12.7-6.8,20.4c0,7.3,2.3,14.1,6.2,19.6c-2.5,1-5.3,1.6-8.2,1.6C41.3,64.5,38.6,63.9,36,62.9z"
/>
</svg>
);
};
Bouncing Balls
The following example shows usage of percentage-based animation and negative delay support.
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>
);
};
Conclusions
The library is released under MIT license so feel free to use it in any commercial product.
I hope you liked it and I'm looking forward for your feedback and suggestions.





Top comments (0)