the
Animating
Virtual DOM
Sarah Drasner
@sarah_edo
Sarah Drasner
@sarah_edo
Consultant
CSS-Tricks,
IBM,
Microsoft, Salesforce
Smashing Magazine,
NetMag,
Zillow, Workflo,
O’Reilly, Frontend Masters, & Mule Design
Microsoft
Sr. Cloud
Developer Advocate
Fiber
- Priority-based scheduling of updates
- Animation update preferred to data update
- Animation becomes a first-class citizen
“We’ve evolved to perform actions that flow more or less seamlessly.
"We aren’t wired to deal with the fits and starts of human-computer interaction.”
Sensory memory: Your occipital lobe (AKA “the memory store”) works in 100ms bursts.
-Tammy Everts
React encapsulates state that's changing
Animation ties these two states together
Users can then follow the trajectory of the change as if it were a
Story
The "so what" factor
User attention span is short.
2 seconds
until dropoff
Amazon has discovered that for every one second delay, conversions dropped by 7%. If you sell $100k per day, that’s an annual loss of $2.5m.
Walmart has found that it gains 1% revenue increase for every 100ms of improvement.
Start with the end
Also helps track what validation looks like:
Google PMs are encouraged to find ONE goal.
Fill in the space between
Aligns the entire team to the same goal
Reduces friction
Without Transitions
Gain understanding
Spatial or otherwise
Workfloapp.com - @workflohq
Percieved Performance
Entire filesize: 6KB!
This actually goes back to IE9
What does the "Scalable" mean?
You don't have to care about positioning, even for responsive
Declarative ANimations
Timing
Workfloapp.com - @workflohq
Timing
<TransitionGroup>
{ this.state.shouldShowSoundwaves &&
<Soundwaves
outTime={0.5}
drawTiming={5}
elTime={1}
easing="Circ"
/>
}
</TransitionGroup>
Timing
Consider how things look to your user
Create Beautiful Defaults
Keep animation consistent and reusable the way you do with text, layout
const Eases = {
entrance: {
animationTimingFunction: `cubic-bezier(0.39, 0.575, 0.565, 1)`,
},
entranceEmphasis: {
animationTimingFunction: `cubic-bezier(0.175, 0.885, 0.32, 1.275)`,
},
exit: {
animationTimingFunction: `cubic-bezier(0.47, 0, 0.745, 0.715)`,
},
exitEmphasis: {
animationTimingFunction: `cubic-bezier(0.6, -0.28, 0.735, 0.045)`,
},
}
const Timing = {
t1: {
animationDuration: 0.1,
},
t2: {
animationDuration: 0.15,
},
t3: {
animationDuration: 0.2,
}...
}
h1, h2, h3, h4, h5 is body copy
t1, t2, t3, t4, t5 is typical timing
Animation iteration
Workfloapp.com - @workflohq
Composite components, including animation
mo.js
mo.js
const burst = new mojs.Burst({
left: 0, top: 0,
zIndex: 10000,
radius: { 0: 100 },
count: 5,
children: {
shape: 'cross',
stroke: { 'cyan' : 'yellow' },
angle: { 360: 0 },
duration: 2000,
delay: 'stagger(0, 100)'
}
});
mo.js
This pen.
class ButtonGroup extends React.Component {
render() {
return (<div></div>);
},
shouldComponentUpdate() {
this.props.isPlaying && this._burst.replay();
return false;
},
componentDidMount () {
const dog1 = new mojs.Burst({
...dog_opts,
children: {
...dog_child_opts,
shape: 'dogbase',
fill: '#E89221'
}
});
...
React-Motion
This pen
This pen.
getStyles(prevStyles) {
const endValue = prevStyles.map((_, i) => {
let staggerStiff = 100, staggerDamp = 19;
return i === 0
? { opacity: spring(this.state.open ? 0 : 1, {stiffness: staggerStiff, damping: staggerDamp}) }
: { opacity: spring(this.state.open ? 0 : 1, {stiffness: (staggerStiff - (i * 7)), damping: staggerDamp + (i * 0.2)}) }
});
return endValue;
},
<StaggeredMotion
defaultStyles = {arr}
styles={this.getStyles}>
{circ =>
<g fill={pathColor} className="cPath">
{circ.map(({opacity}, i) =>
<path
key={i}
d={pathData[i]}
style={{
opacity: opacity
}} />
)}
</g>
}
</StaggeredMotion>
This pen.
Drawn SVG
Done with stroke-dasharray and stroke-dashoffset
- Path or shape has a stroke
- The stroke is dashed
- Use JS to .getTotalLength()
- Dasharray the whole length of the shape
- Animate dashoffset
@keyframes dash {
50% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -274;
}
}
<Motion style={{
//designate all of the differences in interpolated values in these ternary operators
...
dash: spring(this.state.compact ? 0 : 200),
...
}}>
{/* make sure the values are passed below*/}
{({..., dash, ...}) =>
<svg viewBox="0 0 803.9 738.1" aria-labelledby="title">
<title>React-Motion</title>
...
<g style={{ strokeDashoffset: `${dash}` }}
className="react-letters" data-name="react motion letters">
<path className="cls-5" d="M178.4,247a2.2,2.2,0,1,1-3.5,2.6l-6.5-8.7h-8.6v7.4a2.2,2.2,0,0,1-4.4,0V220.1a2.2,2.2,0,0,1,2.2-2.2h10.8a11.5,11.5,0,0,1,4.8,22Zm-18.6-10.3h8.6a7.3,7.3,0,0,0,0-14.7h-8.6v14.7Z" transform="translate(3.1 1.5)"/>
...
</g>
</svg>
}
</Motion>
correct tools for the job
my recommendations:
CSS-in-JS/Styles
- Small sequences and simple interactions
- Once you get more than 3... switch to:
- Great for sequencing and complex movement
- Cross-browser consistency
- Great for single movements that you'd like to look realistic
- React-Tween similar to React-Motion: time instead of spring
- React-Move similar to React-Motion: small, flexible
- Snap.svg is more like jQuery for SVG
- Web Animations API looks great, still waiting on support
- Velocity-React is similar to GSAP with less bells and whistles
- Mo.js spins up shapes declaratively and is still in beta
correct tools for the job
GSAP (GreenSock)
It's very tough to find something another tool can do that GSAP can't do...
but there are plenty of things GSAP can do that other tools can't.
🏆
- Transforms
- Cross-browser inconsistencies
- Morphing
- Sequencing and chaining
Not all are created equal
- Opacity
- Transforms
- Hardware Acceleration
const accelerate = {
transform: 'translateZ(0)',
backface-visibility: 'hidden',
perspective: '1000px',
}
/* 3rd 2nd 1st */
transform: rotate(90deg) translateX(30px) scale(1.5);
Stacking Transforms
@keyframes foo {
30% {
transform: rotateY(360deg) scale(1.23) translateY(-14px)
}
65% {
transform: rotateY(-360deg) scale(1.5) translateY(-30px)
}
90% {
transform: rotateY(-102deg) scale(0.75) translateY(10px)
}
}
Stacking Transforms
womp womp :(
TweenMax.to('.thing', 2, {
x: 20,
y: 100,
scale: 20,
skew: 2
})
GreenSock
Don't have to write transform: translateX... etc either.
Or hardware accelerate.
Not beholden to the spec
🔥
*React-Move/React-Motion/React-Tween can handle matrix transforms, it's just not very intuitive
Animate the unanimatible, like SVG filters
Solves Cross-Browser Inconsistencies
Bad transform origin bug on rotation, soon to be solved in Firefox.
More in this CSS-Tricks article.
Chrome
IE
Firefox
Safari (zoomed)
Solves Other Transform-Origin Issues
This pen.
MorphSVG
Sequences
& Chaining
Easily spaghetti:
Delays for chaining,
Callbacks
Timeline
- stack tweens
- change their placement in time
- group them into scenes
- add relative labels
- animate the scenes!
- reuse the scenes, play backwards, pause
//instantiate a TimelineLite
const tl = new TimelineMax();
//adds a tween at the beginning
tl.to(el1, 0.5, {x:100, opacity:0})
//adds another tween immediately after
tl.to(el2, 0.5, {y:-100, opacity:0})
//schedule next tween 0.5 seconds after
tl.to(el3, 0.5, {scale:.5}, "+=0.5")
End to end
Leo Leung
React transition Group Plus
&&
Greensock
💥
===
Transitions
Don't reinvent the wheel / Avoid callback hell
Without in-out modes
End to end
State-driven ANIMAtions
toggleShape() {
if (this.state.screen === 0) {
this.animFire(this.state.splitText)
} ...
this.setState({
screen: (this.state.screen + 1) % 3
})
}
State-driven Animations
animFire(splitText) {
const tl = new TimelineMax,
stA = 'start';
TweenMax.set([this.g1.childNodes, this.g2.childNodes], {
clearProps:'svgOrigin'
})
tl.add('start')
tl.staggerFromTo(this.g1.childNodes, dur, {
drawSVG: '68% 100%'
}, {
drawSVG: '27.75% 0%',
ease: Back.easeOut
}, stD, stA)
...
}
componentWillEnter(callback) {
const { drawTiming, elTime, easing } = this.props,
elTime2 = elTime * 2;
TweenMax.fromTo(this.box, elTime2, {
opacity: 0,
drawSVG: '50% 50%'
}, {
opacity: 1,
drawSVG: '100%',
ease: `${easing}.easeOut`
})
...
}
the sky's the limit!
More!
Draggable
Motion Along a Path
Custom Easing
Physics/ThrowProps
React encapsulates state that's changing
Animation ties these two states together
💥
SVG Animations
with Val Head
🎉
Thank you!
@sarah_edo on twitter
Animating the Virtual DOM
By sdrasner
Animating the Virtual DOM
- 28,629