React UI animation components built on top of f1-dom
and f1
.
- Create complex animations with custom delays, durations, eases
- Animate individual properties (scale, opacity, etc.) independently of each other eg. delay the opacity animation by 0.5 seconds but scale immediately
- Special handling for properties such as css colors and transforms (translate, scale, rotate) to allow for easier animation
- True separation of concerns. Animations defined outside of application implementation (less Spaghetti code)
- Absolute control over animations from page transitions down to individual ui components
- Uses path finding to figure out how to animate from one state to another. For instance how to animate from a button from an out state to a pressed state (write less logic for complex animations)
There are two components which are exposed in this module:
var ReactF1 = require('react-f1');
// and
var Chief = require('react-f1/Chief');
ReactF1
can be used to create complete animated UI components such as buttons, toggles, accordians, etc.
Chief
is used to control ReactF1
components. For instance when a page animate's in one Chief
component can tell all button's (which are ReactF1
components or other Chief
components) on a page to animate in staggered/delayed from one another.
$ npm i react-f1 react react-dom --save
There is an example folder distributed with this module. It contains two examples. One being a small example of how to use ReactF1
and the other on how to use Chief
.
Details on how to run these examples are noted below. Once running in your browser you can simply edit the js files listed below and see changes immediately in browser.
This example builds and renders a small animated button that you can see in use above.
$ npm run example-f1
Below is a description of all example files that you might want to edit. All of these files have comments which explain each piece of the application:
example/f1/ExampleButton.js: Example Button Component built using ReactF1.
example/f1/getStates.js: a function which returns an Object which defines what the button should look like in each state.
example/f1/getTransitions.js: a function which returns an Array which defines how to animate between states.
The above example uses two components SelectIndicator
(small line that moves up and down on the left) and FancyButton
which is the buttons on the right. States and logic for selected buttons are handled by Chief
.
To run the Chief example:
$ npm run example-chief
example/chief/Menu/: This folder contains all code including states and transitions used by Chief
to create a Menu. (this is the menu in the above gif)
example/chief/FancyButton/: This folder contains all code including states and transitions used by the FancyButton. (buttons on the right side of the menu in the above gif)
example/chief/SelectIndicator/: This folder contains all code including states and transitions used by the SelectIndicator. (small line that moves up and down in the above gif)
The following describes on a high level how ReactF1 and Chief components are used.
<ReactF1
// state here is a String
// it is the state that this ui component should
// animate to using path finding
go={state}
// onComplete is a Function which will be called when
// we're in the state passed to `go`
onComplete={onComplete}
// states is an Object which defines what this state
// ui should look like in each state
// eg. in idle, mouse over, pressed, etc.
states={states}
// transitions is an Array which defines how ReactF1 should
// animate between states
transitions={transitions}
// component is a string which determines the wrapper element
// It defaults to div but can be set to any dom element
component="div"
>
// the following defines components which will be animated
// data-f1 associates these components with properties which
// be defines in the states Object described above
<div data-f1="targetName1" />
<div data-f1="targetName2" />
</ReactF1>
// this Object describes what this ui should look like in each state
var states = {
// this is the name of a state for instance this could be idle
stateName1: {
// this describes what a piece of the ui should look like in this state
// it links to an item above described through data-f1
targetName1: {
// the following will describe what css styles should look like
// for targetName1 in this state
style: {
// it should be noted that in order to have predictable behaviour with
// react-f1 you should define each css property in each state that
// maybe animated at some point.
// eg. `left` css property in out is set to 0, idle `left` is 0 again,
// and finally in the over state `left` is 100xw
cssProperty1: value,
cssProperty2: value
}
},
// this describes another piece of ui associted via the
// data-f1 attribute
targetName2: {
style: {
cssProperty1: value,
cssProperty2: value
}
}
},
// this is the name of a state for instance this could be mouseOver
stateName2: {
targetName1: {
style: {
cssProperty1: value,
cssProperty2: value
}
},
targetName2: {
style: {
cssProperty1: value,
cssProperty2: value
}
}
}
}
var transitions = [
// to define that it's possible to animate from one
// state to another.
// If no animation is defined then a default animation
// will be used
{ from: 'stateName1', to: 'stateName2' },
// Transitions are not bi-directional
// which means we will need to do this also
// { from: 'stateName2', to: 'stateName1' }
// however as a short form you can do
// { from: 'stateName1', to: 'stateName2', bi: true }
{
from: 'stateName2', to: 'stateName1',
// the following will define the animation to go from
// stateName2 to stateName1
animation: {
// the following would define that all parts of the ui
// will animate in 0.5 seconds and be delayed by 0.1 seconds
duration: 0.5,
delay: 0.1,
// this will define that all animations will use this easeFunction
// typically this would be one of the ease functions from:
// https://www.npmjs.com/eases
ease: easeFunction1,
// the following would override the duraration and delay
// which were defined globally just for targetName1
targetName1: {
duration: 0.3,
delay: 0
},
// you can also override animations all the way down to a
// a property.
// In this case everything for targetName2 would use the globally
// defined animation settings:
// duration 0.5 seconds and be delayed 0.1 and use easeFunction1
// however you can override down to a single animatable property
// in thise case cssProperty1 will have duration of 0.25, delay 0.2,
// and use easeFunction2 for easing
targetName2: {
style: {
cssProperty1: {
duration: 0.25,
delay: 0.2,
ease: easeFunction2
}
}
}
}
}
]
// Chief from an API perspective looks very much the way that ReactF1 does
<Chief
// state which we should animate to
go={state}
// callback when we reach that state
onComplete={onComplete}
// states for Chief define what state all ui should be in
states={states}
// transitions can define delays for triggering ui states
transitions={transitions}
>
{
// with React it's very hard to manipulate deeply nested (grand child)
// component's properties.
// This is why you need to pass in a function that will accept an Object
(states) => {
// this function needs to return a single root component
return <div>
// for simplicity this example does not defines states or transitions
// for these ReactF1 ui components however states.target1 does
// contain a variable called go and a function onComplete which
// will be the state in which this ui Component should be
<ReactF1
// states.target1 might look like this
// { go: 'idle', onComplete: callback }
{...states.target1}
/>
<ReactF1
// states.target2 might look like this
// { go: 'idle', onComplete: callback }
{...states.target2}
/>
// It should be Noted also that a Chief component can control other
// Chief's
<Chief
// states.target3 might look like this
// { go: 'idle', onComplete: callback }
{...states.target3}
/>
</div>;
}
}
</Chief>
var states = {
// this will define what state each of the ui components
// should be in during the out state
out: {
target1: 'out',
target2: 'out',
target3: 'out'
},
// this will define what state each of the ui components
// should be in during the idle state
idle: {
target1: 'idle',
target2: 'idle',
target3: 'idle'
}
};
var transitions = [
// this defines a path for Chief to go from out to idle
// if no animations are passed then all ui's states will
// be set at the same time.
//
// It should be noted you can also do this:
// { from: 'out', to: 'idle', bi: true }
// Which will create a bi-directional transition.
{ from: 'out', to: 'idle' },
// you may want to "stagger" ui going to a state
// this is how you'd do it
{ from: 'out', to: 'idle', animation: {
target2: {
delay: 0.25
},
target3: {
delay: 0.5
}
}
}
];
MIT, see LICENSE.md for details.