Composable animation

The Goal

Communicating @sarah_edo

? @sarah_edo

Happiness sadness Fear curiosity @sarah_edo

Happiness sadness Fear curiosity @sarah_edo

@sarah_edo

@sarah_edo

Anything can happen

SARAH DRASNER @SARAH_EDO

Branding And personality

@sarah_edo

dribbble.com/leoleung @sarah_edo

tympanus.net/Development/ImageRevealHover/ @sarah_edo

Easing

codepen.io/sdras/pen/pyedJE css-tricks.com/comparison-animation-technologies/

@sarah_edo

codepen.io/sdras/pen/qOdwdB @sarah_edo

Create beautiful defaults (from THE Jina!) @sarah_edo

@sarah_edo

@sarah_edo

Chris Gannon gannon.tv/products/download-dot-bounce @sarah_edo

codepen.io/sdras/pen/LEorev @sarah_edo

Having an opinion @sarah_edo

dribbble.com/rally @sarah_edo

dribbble.com/outcrowd @sarah_edo

dribbble.com/uigreat @sarah_edo

dribbble.com/Yar_Z @sarah_edo

dribbble.com/advancedgroup @sarah_edo

codepen.io/sdras/pen/gWWQgb @sarah_edo

Look around you Study reality

Unpopular opinion: Stop using material design as your motion design

codepen.io/sdras/pen/JbaGwg @sarah_edo

“It’s important to define your audience for your documentation early.” Val head, designing interface animation

Have a bdfl ... but also support @sarah_edo

Composing Creating Beautiful Defaults @sarah_edo

codepen.io/sdras/pen/qqVrxy @sarah_edo

Timing Like h1, h2 //---timing ----// $class-slug: t !default; @for $i from 1 through 7 { .#{$class-slug}-#{$i} { animation-duration: 0.8 - (0.1s * $i); } } @sarah_edo

Easing Branded //---ease ----// $easein-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53); $easeout-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94); $easein-back: cubic-bezier(.57, .07, .6, 1.71); $easeout-back: cubic-bezier(0.175, 0.885, 0.32, 1.275); .entrance { animation-timing-function: $easeout-quad; } @sarah_edo

Animations Only what you need //animations @keyframes pop { 0% { transform: scale(0.9) translateZ(0); } 100% { transform: scale(1) translateZ(0); } } .pop { animation-name: pop; @extend .anim-fill-both; } @sarah_edo

@sarah_edo

@sarah_edo

@sarah_edo

<TransitionGroup> { this.state.shouldShowSoundwaves && <Soundwaves outTime={0.5} drawTiming={5} elTime={1} easing=‘Circ’ /> } </TransitionGroup> @sarah_edo

Vue Single File Components <template> <div> </div> </template> <script> export default { } </script> <style scoped> </style> @sarah_edo

Import Global Sass Files for Overarching Principles module.exports = { css: { loaderOptions: { sass: { data: @import "@/scss/_variables.scss"; } } } } css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/ @sarah_edo

@sarah_edo

<template> <svg @click="makeHeart" xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 120 120" aria-labelledby="heartface" role="presentation"> ... </svg> </template> <script> import { TweenMax, TimelineMax, Sine } from ‘gsap' export default { methods: { makeHeart() { const tl = new TimelineMax() tl.add('start') ... } } } </script> github.com/sdras/vue-sample-svg-icons @sarah_edo

Responsive interactions Desktop and mobile compatible Zingtouch Hammer Draggable Flickity onMouseOver || onTouchStart @sarah_edo

Responsive consistency • SVG is great for responsive • Eases should stay the same, but timing can be adjusted • initial-scale=1.0 in the meta tag or device will wait 300ms • Larger touch-target, > 40px x 40px or use @media(pointer:coarse) 24ways.org/2016/animation-in-design-systems/ @sarah_edo

page transitions

@sarah_edo

Templates in the pages directory <nuxt-link to="/product">Product</nuxt-link> @sarah_edo

@sarah_edo

Transition hook already available name=“page" .page-enter-active, .page-leave-active { transition: all .25s ease-out; } .page-enter, .page-leave-active { opacity: 0; transform: scale(0.95); transform-origin: 50% 50%; } .page-enter-active, .page-leave-active { transition: all .25s ease-out; } .page-enter, .page-leave-active { opacity: 0; transform: scale(0.95); transform-origin: 50% 50%; } @sarah_edo

@sarah_edo

Js hooks export default { transition: { mode: 'out-in', css: false, enter (el, done) { let tl = new TimelineMax({ onComplete: done }), spt = new SplitText('h1', { type: 'chars' }), chars = spt.chars; TweenMax.set(chars, { transformPerspective: 600, perspective: 300, transformStyle: 'preserve-3d' }) } tl.add('start') tl.from(el, 0.8, { scale: 0.9, transformOrigin: '50% 50%', ease: Sine.easeOut }, 'start') ... tl.timeScale(1.5) @sarah_edo

@sarah_edo

Repo https://github.com/sdras/nuxt-type Demo https://nuxt-type.now.sh/ @sarah_edo

Native-like page transitions

@sarah_edo

@sarah_edo

@sarah_edo

In store/index.js import Vuex from 'vuex' const createStore = () =>" { return new Vuex.Store({ state: { page: 'index' }, mutations: { updatePage(state, pageName) { state.page = pageName } } }) } export default createStore @sarah_edo

In middleware/pages.js: export default function(context) { // go tell the store to update the page context.store.commit('updatePage', context.route.name) } register the middleware in nuxt.config.js: module.exports = { … router: { middleware: 'pages' } } @sarah_edo

In layouts/default.vue: <template> <div> <app-navigation /> <nuxt/> </div> </template> In AppNavTransition.vue: <h2 key="profile-name" class="profile-name"> <span v-if="page ===$$ 'group'" class="user-trip">{{ selectedUser.trip }}</span> <span v-else>{{ selectedUser.name }}</span> </h2> @sarah_edo

<transition-group /> Flip under the hood Rosario’s article @sarah_edo

Transition Group wraps it... <transition-group name="layout" tag="g"> <rect class="items rect" ref="rect" key="rect" width="171" height="171"/> ... </transition-group> Styles: .items, .list-move { transition: all 0.4s ease; } .active { .rect { transform: translate3d(0, 30px, 0); } ... } @sarah_edo

@sarah_edo

My Demo vue/nuxt page-transitions.com github.com/sdras/page-transitions-travelapp Simona Cotin fork angular/typescript github.com/simonaco/page-transitions-travelapp Shawn wang fork react/next github.com/simonaco/page-transitions-travelapp @sarah_edo

Animate CSS Grid github.com/aholachek/animate-css-grid/ @sarah_edo

Give life to our work

Thank you! @sarah_edo