Documentation

Documentation

JavaScript

Vue

Integrate Motion with Vue 3

Motion is fully framework agnostic, therefore it works within any JavaScript environment, including Vue 3. In this guide, we'll learn how to:

  • Animate on enter and exit

  • Perform view/layout animations

  • Animate timeline sequences

  • Animate when props change

  • Change HTML and CSS when an element enters or leaves the viewport

  • Animate on scroll

Install

Install Motion into the Vue project.

npm

Enter and exit animations

To animate elements when they enter and exit the DOM, we can use Vue's Transition component.

<template>
  <Transition :css="false">
    <h1>Hello world</h1>
  </Transition>
</template>

<style>
  h1 { opacity: 0; }
</style>

Start by creating an onEnter function with Motion's animate function.

<script setup>
  import { animate } from "motion"

  async function onEnter(el, onComplete) {
    await animate(el, { opacity: 1 })
    onComplete()
  }
</script>

You can now provide this to Transition with the @enter event.

<Transition :css="false" @enter="onEnter">

Likewise, to animate when a component exits, you can make a onLeave function.

<script setup>
  import { animate } from "motion"

  async function onLeave(el, onComplete) {
    await animate(el, { opacity: 0 })
    onComplete()
  }
</script>

Then pass this to the @leave event.

<Transition :css="false" @leave="onLeave">

View animations

Motion's powerful view() function (currently in Motion+ early access) can perform full page transitions, animate between different layouts and even between different elements.

By await-ing Vue's nextTick function within view's update function, we ensure the view animation starts after the page has been updated.

<script setup>
  import { ref, nextTick } from "vue"

  const isOpen = ref(false)

  function openModal() {
    view(async () => {
      isOpen.value = true

      await nextTick()
    }).enter({ opacity: 1 })
  }
</script>

<template>
  <button @click="openModal">Open modal</button>
  <div :style="{display: isOpen ? 'block' : 'none'}" class="modal"></div>
</template>

Animate timeline sequences

onEnter and onLeave are passed the element provided as the first Transition child, in this case the ul:

<Transition :css="false" @enter="onEnter" @leave="onLeave">
  <ul>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</Transition>

This is useful for building timeline animations scoped to the component:

async function onEnter(el, onComplete) {
  const sequence = [
    [el, { opacity: 1 }],
    [el.querySelectorAll("li"), { y: 100, opacity: 1 }]
  ]
  
  await animate(sequence)
  onComplete()
}

Animate on prop change

Using Vue's watch function and refs, we can start watching changes in props passed into the component to trigger animate calls.

import { useTemplateRef, defineProps, watch, onWatcherCleanup } from "vue"

const header = useTemplateRef("header")

const props = defineProps({
  opacity: { type: Number, default: 1 }
})

watch(props, async () => {
  const animation = animate(header.value, { opacity: props.opacity })
})

Add a cleanup function to ensure animations are stopped when the component's removed.

import { animate } from "vue"

watch(props, async () => {
  const animation = animate(header.value, { opacity: props.opacity })
  
  onWatcherCleanup(() => animation.stop())
})

Viewport detection

With Motion's inView function, it's possible to detect when an element enters or leaves the viewport and change its appearance accordingly.

Start by adding a ref to track visibility state:

<script setup>
import { ref, useTemplateRef } from "vue"
  
const isInView = ref(false)
const container = useTemplateRef("container")
</script>

We can attach a "container" ref to an element in our template:

<template>
  <div ref="container"></div>
</template>

We can now pass this element to inView within an onMounted lifecycle hook.

onMounted(() => {
  inView(container.value, () => {
    isInView.value = true

    return () => {
      isInView.value = false
    }
  })
})

Finally, we want to make sure we clean up when the element's removed using the onUnmounted hook:

import { ref, onMounted, onUnmounted } from "vue"
import { inView } from "motion"

const isInView = ref(false)
const container = useTemplateRef("container")
let stopViewTracking

onMounted(() => {
  stopViewTracking = inView(container.value, () => {
    isInView.value = true

    return () => {
      isInView.value = false
    }
  })
})
  
onUnmounted(() => stopViewTracking())

With this state now changing as the element enters/leaves the viewport, we can use this isInView state to animate with CSS by swapping out classes:

<template>
  <div ref="container" :class="{ 'in-view': isInView }"></div>
</template>

<style scoped>
  div {
    background-color: blue;
    transition: background-color 0.5s linear;
  }
  
  .in-view {
    background-color: red;
  }
</style>

Or to render different HTML entirely:

<div ref="container">
  <p v-if="isInView">Element in view</p>
  <p v-else>Element out of view</p>
</div>

Scroll-triggered animations

We can also use inView to trigger the animate function itself. This can be useful if we want to trigger more complex animation sequences or animate transforms independently of each other.

onMounted(() => {
  stopViewTracking = inView(container.value, () => {
    animate(container.value, { scale: 1.2 })

    return () => {
      animate(container.value, { scale: 1 })
    }
  })
})

Scroll-linked animations

The scroll function can be used to link animations and functions to scroll progress by using Vue's template refs and lifecycle functions as before.

import { scroll, animate } from "motion"
import { onMounted, onUnmounted } from "vue"

let stopScrollAnimation

onMounted(() => {
  stopScrollAnimation = scroll(
    animate(container.value, { transform: ["none", "rotate(90deg)"] })
  )
})

onUnmounted(() => stopScrollAnimation())

Next

With Motion set up in your Vue project, we recommend you follow the rest of the Quick Start guide to begin learning how to use Motion's animate, scroll and inView functions.

A Motion+ membership will give you early access to features & content, access to our private Github and Discord, and more.

It's a one-time fee that grants lifetime access. No subscription necessary!

MOTION+

Introducing Cursor

Cursor is a creative cursor component for React, that makes it super easy to make custom cursor and follow cursor effects.


Hover over these examples to check out some of what it can do:

Custom cursor

Follow with spring

Multicursor

?

?