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%
) -
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)
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 ofObserver
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>
);
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)