Animations beyond CSS

Prague 21 Sept18

GitHub icon
Twitter icon

Eduardo

San Martin Morote

Freelance Dev & Instructor

Vue core team

๐ŸŠโ€โ™‚๏ธ โŒจ๏ธ ๐ŸŒ

GitHub icon

Demo

Made with Nuxt!

Animations are hard

Activity monitor

Animations should be fast

Hi there ๐Ÿ‘‹

I'm taking

my time

to appear...

Animating when...

  • The user does something
  • We need user's attention
  • We want to make things fun

<Transition/>ย 

<Transition-group/>

Based on

  • Entering/Leaving the page (v-if, v-show, key)
  • Reordering (v-for)

Using

CSS or

Javascript

CSS transitions

.fade-in, .fade-out {
  transition: opacity 300ms;
}

.fade-out {
  opacity: 0;
}

CSS animations

.my-element {
  animation: moving 1s infinite alternate;
}

@keyframes moving {
  0% { transform: translateX(0); }
  100% { transform: translateX(200px); }
}

Transition + Animate.css

๐Ÿ‘Ž rotation, translation, scaling

๐Ÿ‘ opacity, colours, etc

๐Ÿ‘‰ Basic Transition

Transition + ANimate.css

<transition
  enter-active-class="animated slideInLeft"
  leave-active-class="animated slideOutRight fadeOut"
>
  <div v-if="on" key="onDiv">On</div>
  <div v-else>Off</div>
</transition>

Limited to

  • Elements appearing
  • Elements disappearing
  • Elements reordering
  • CSS properties

CSS animations

  • transition
  • animation
transition: opacity 1s;
0
1
animation: fade 1s;
0
1
.8

(keyframes)

State Animations

What is it?

How to go from one state to another

ย 

  • Easings
  • Physicsย 

Easing

Go from 10ย to 100 Bouncing outย in 1 second

10: initial value

100: target value

Bouncing out: Easing

1s: duration

๐Ÿ‘‰ Easing graph

Inspired by easings.net

SPRING

Go to 100ย quickly and no oscillation

100: target value

quickly: high stiffness

no oscillation: low damping

๐Ÿ‘‰ Motion graph

Easing

  • highly customisable
  • many existing easings
  • custom duration
  • easy to chain
  • playback control

Spring

  • only two parameters
  • always feels natural
  • handles interruptions/changes

Let's create a State animation with Tween.js

(or any other tweening lib)

// setup the animation loop
// (do it somewhere, but only once)
function animate(time) {
  requestAnimationFrame(animate)
  TWEEN.update(time)
}
requestAnimationFrame(animate)

const container = { x: 0 } // Tween modifies this
const tween = new TWEEN.Tween(container)
  .to({ x: 300 }, 1000) // change to 300 in 1s
  .easing(TWEEN.Easing.Quadratic.Out)
  .onUpdate(() => {
    // container.x is updated
  })
  .start() // Start the tween immediately.

Necessary boilerplate to create one single tween ๐Ÿ˜ฑ

Tweening with

Vue tweezing

Polygon example

Originally from docs

by @chrisvfritz

๐Ÿ‘‰ Polygon

โœจ Declarative Tweening

<Tweezing
  :to="stats"
  :duration="updateInterval"
  :easing="easing"
>
  <polygon
    slot-scope="pointsArray"
    :points="transformToPoints(pointsArray)"
  ></polygon>
</Tweezing>

Declarativity

Focus on how the state maps to your components, not on how to keep everything in sync

Easings controlled by scroll/Mouse

Using the scroll instead of time as input

๐Ÿ‘‰ Easing with mouse

โœจ Declarative Tweening

<Tweezing
  :to="1" tween="custom"
  :time="mouseYPer"
>
  <div slot-scope="value">
    <pre>{{ mouseYPer }}%</pre>
    <div class="ball" :style="ballStyle(value)">
    </div>
  </div>
</Tweezing>

Tweezing component

  • Watch target value โžก๏ธ create Tween

  • Provide tweened value with a scoped slot

this.rotation // 10
this.rotation = 220
// triggers a new tweening

Tween engines

import {
  Tweezing,
  tweenjsHelper,
} from 'vue-tweezing'
import TWEEN from '@tweenjs/tween.js'

Vue.use(Tweezing, {
  tweenjs: tweenjsHelper(TWEEN),
})

Tween.js

export function tweenjsHelper (TWEEN) {
  return function (value, end, opts) {
    const container = { value }
    // cancel previous tween
    return new TWEEN.Tween(container)
      .to({ value: end }, opts.duration)
      .interpolation(opts.interpolation || TWEEN.Interpolation.Linear)
      .easing(opts.easing || TWEEN.Easing.Quadratic.Out)
    // TODO should probably emit the name of the property too
    // default could be the name if only one value is provided
      .onStart(() => this.$emit('start'))
      .onUpdate(() => {
        opts.$setValue(container.value)
      })
      .onComplete(() => this.$emit('end'))
      .start()
  }
}

Tweezing

๐Ÿ—œ< 1kb

Springs

with

vue motion

svg Chart

๐Ÿ‘‰ Plot

โœจ Declarative Spring

<Motion tag="g" :values="selectedValues">
  <template slot-scope="values">
    <g v-for="(y, i) in values" class="bars">
      <rect
        :x="i * 10 + 20"
        :y="getMax - y * 10"
        width="10"
        :height="y * 10"
      />
    </g>
  </template>
</Motion>

Lazy sudoku example

Originally from docs

by @chrisvfritz

๐Ÿ‘‰ Sudoku

"Original"

with springs

โœจ Declarative Spring

<Motion :values="positions" spring="wobbly">
  <template slot-scope="positions">
    <div v-for="cell in cells"
      :style="{\
        transform: `translate(\
          ${positions[cell.id].x}px,\
          ${positions[cell.id].y}px)\
      `}"
    >
      {{ cell.number }}
    </div>
  </template>
</Motion>

๐Ÿคจ

๐Ÿ”ˆ Sounds

๐Ÿ™‰

Declarative Music

<Music
  src="meatball-parade.mp3"
  :rate="rate"
/>

Declarative Animated Music

<Motion :value="rate" :spring="springConfig">
  <Music
    scoped-slot="{ value }"
    src="meatball-parade.mp3"
    :rate="value"
  />
</Motion>

Other
visual renderers

Declarative 3D Scene

<Renderer :width="width" :height="height">
  <Scene>
    <Camera :position="camPos"/>
    <AmbientLight/>
    <SpotLight :position="lightPosition"/>
    <Cube
      v-for="cube in cubes"
      :key="cube.id"
      :position="cube.position"
    />
    <Ground/>
  </Scene>
</Renderer>

๐Ÿ•บDancing Cubes

<Motion v-for="(cube, i) in cubes"
  :values="cube" :key="i"
>
  <Cube
    slot-scope="cube"
    :position="cube.position"
  />
</Motion>

Getting started

GitHub icon

Prefer CSS Over
Javascript

Browsers + GPU โ™ฅ๏ธ Pure CSSย 

Thanks! ๐Ÿ––

GitHub icon