Animated react transitions 💫
Simple & declarative transition animations for React.
THIS IS EARLY WORK IN PROGRESS AND NOT “RELEASED TO PUBLIC” YET, THOUGH THIS SHOULD HAPPEN ANYTIME SOON.
FEEL FREE TO WATCH 👓, STAR ⭐ OR CONTACT ME TO BE NOTIFIED ABOUT PROGRESS.
TODO: Video / gif here
Ever wanted to implement one of those incredible designs you find on dribble or Muzli where one element beautifully transforms into another upon page transition? And then realized “But I want my code to stay statefull, decoupled, scalable, declarative”, so you ended up with regular “hard cuts” instead? – To me this happend very very often!
react-dip
solves this by providing animated transisions in an effortless way, that just worksTM, by using the FLIP technique.
$ yarn add react-dip
# or using npm
$ npm install --save react-dip
Using a module bundler like webpack or parcel, import your depency just as any other:
// Using ES6 modules
import Dip from 'react-dip'
// or using CommonJS modules
var Dip = require('react-dip')
UMD builds are also available via unpkg:
<script src="https://unpkg.com/react-dip/dist/react-dip.umd.min.js" ></script>
The API is as small as possible, almost everything is optional, so you see results immediately. Just wrap your Items in a Dip
import React, {Component} from 'react'
import Dip from 'react-dip'
function Component1() {
return (
<Dip dipId="quickStart" style={{background: 'red'}}>
some content
</Dip>
)
}
function Component2() {
return (
<Dip
dipId="quickStart"
style={{position: 'absolute', top: '100px', background: 'green'}}
>
some other content <br />
etc...
</Dip>
)
}
// use complex state here
// or a routing solution such as react-router
// or connect it to redux, or ustated
export default class MyStatefulParent extends Component {
state = {currentStep: 0}
toggleState = () =>
this.setState(state => ({
currentStep: (state.currentStep + 1) % 2,
}))
render() {
return (
<section>
<h1> Quick Start example </h1>
<button onClick={this.toggleState}>toggle me</button>
{this.state.currentStep === 0 ? <Component1 ></Component1> : <Component2 ></Component2>}
</section>
)
}
}
Note: Using inline styles
as well as absolute
positioning is usually not considered a good way and is applied here for the sake of simplicity.
You can use any type CSS
or CSS-in-JS
styling and fluid
/ flex
/ grid
layout.
The API surface is intetended to be as simple as possible. Only dipId
and children
(or render
) are required props. The rest is optional and helps you fine tuning your animations.
string
| Required!
The id
that groups two different dip elements. React-dip will only create animated transitions between to Elements with the same dipId
, consider them as potential from- and to-hints.
React Element
| Required unless usingrender
-prop!
Content that is rendered as part of that Dip
.
function({ref: function(), styles: {}})
| Required unless usingchildren
-prop!
Function that should return the content that is rendered as part of that Dip
. Allows for more advanced pattern and skips the wrapping Element. See render prop for further details.
Warning: <Dip render>
takes precedence over <Dip children>
so don’t use both in the same <Dip ></Dip>
.
number
| optional, defaults to200
Time in milliseconds the animation will take when transitioning to this dip.
string
| optional, defaults to"ease-out"
Specifies the desired timing function. Accepts the pre-defined values linear
, ease
, ease-in
, ease-out
, and ease-in-out
, or a custom cubic-bezier
value like cubic-bezier(0.42, 0, 0.58, 1)
.
string
| optional, defaults to"div"
Specify the desired HTML-tag here (eg. <Dip element="li">
) in case you don’t want your children wrapped in a div
.
Array(string)
| optional, defaults to an emptyarray
By default react-dip
will morph your components only regarding their sizes and positions using css transforms
which is usually a good default regarding performance.
In case you want to morph more css properties you can specify them here, such as optInCssStyles={["borderRadius", "backgroundColor"]}
. optional
default
react
/HTML attributes
| optional
Any provided standard attribute is passed to the child-container.
As some browsers do not support the Web Animations API, we recommend using the web-animations-js Polyfill.
$ yarn add web-animations-js
# or using npm
$ npm install --save web-animations-js
import('web-animations-js') // We recommend dynamic imports to keep initial bundlesize small
The basic idea is the following: Every candidate for an animated transition is wrapped in a <Dip ></Dip>
Container,
whereas potential start and destination elements get grouped via the same dipId
property. Say, if you want to animnate a users name from a list to a detail view
you’d wrap the name in both views in a <Dip dipId={
name-${username}}>
Container.
Wrapping the Components in a Dip
-Container does a couple of things:
If the check for existing registered elements was successfull, we kick in the animation logic, based on the FLIP-technique:
Whilst a clone has some downsides, such as creating new DOM-Elements just for animating and possible variations in the styling if nested css selectors were used
it gives us more freedom eg. when animating to an element which is inside of a container with overflow: hidden
.
No DEEEE-EYE-PEEE, just dip your taco into some tasty salsa. 🌮
Chrome | Firefox | Safari | Edge | IE | iOS |
---|---|---|---|---|---|
✅ | ✅ | ✅* | ✅* | ✅* | ✅* |
h1
which is styled via section h1
(might be fixable a little bit, not sure if it is worth though, as it is somehow considered bad practices anyhow?)There are tons of ideas for improving react-dip
such as adding fine grained control to your transitions, but the primary goal will stay to keep the API as symple as possible.
overflow: hidden
and avoiding z-index issuesplaceholder
component that is shown whilst animatingz
-wise)