Vue 3 Recipe • Production Motion Pattern

Vue Scroll Animations: Declarative Viewport Scrubbing & Sequences in Vue

Scroll animations transform static web pages into engaging, narrative digital journeys. In standard Vue development, orchestrating scroll-driven transitions typically involves attaching scroll event listeners or setting up complex IntersectionObserver observers that trigger component re-renders on every scroll tick. This frequently leads to scroll-jacking, layout jitter, and battery drain on mobile devices. ExodeUI introduces declarative scroll animation primitives for Vue 3 that listen to passive window scroll events and stream normalized progress directly into GPU uniform registers. You can scrub complex multi-layer vector timelines, parallax depths, and 3D rotations effortlessly with zero frame drops.

Vue 3 Live Interactive Stage
exodeui-vue
Simulated Scroll Progress: 45%Playback Head: 0.45
Under the Hood: Vue 3 Reactivity & WebGL Pipeline

ExodeUI for Vue connects Vue 3 reactive ref and computed primitives directly to GPU uniform registers and state machines, eliminating Virtual DOM recalculation overhead and guaranteeing consistent 120fps motion.

Runnable ExodeUI Vue 3 Code Example

Vue 3 SFC Composition API

Import the ExodeUICanvas component and bind reactive parameters declaratively without imperative DOM lifecycle hooks:

html
<template>
  <div class="scroll-wrapper" ref="wrapperRef">
    <div class="sticky-viewport">
      <ExodeUICanvas
        ref="canvasRef"
        src="/scenes/vue-scroll-story.json"
        :inputs="{ 'scrollProgress': progress }"
      />
      <div class="progress-bar" :style="{ width: progressPercent }"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { ExodeUICanvas } from 'exodeui-vue';

const wrapperRef = ref(null);
const progress = ref(0);
const progressPercent = computed(() => `${(progress.value * 100).toFixed(1)}%`);

const handleScroll = () => {
  if (!wrapperRef.value) return;
  const rect = wrapperRef.value.getBoundingClientRect();
  const totalScroll = rect.height - window.innerHeight;
  const current = -rect.top;
  progress.value = Math.min(Math.max(current / totalScroll, 0), 1);
};

onMounted(() => {
  window.addEventListener('scroll', handleScroll, { passive: true });
  handleScroll();
});

onUnmounted(() => {
  window.removeEventListener('scroll', handleScroll);
});
</script>

<style scoped>
.scroll-wrapper {
  height: 300vh;
}
.sticky-viewport {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

Frequently Asked Questions about vue scroll animations

Is ExodeUI scroll animation compatible with Nuxt 3 SSR?

Yes. ExodeUI gracefully handles SSR hydration: the canvas mounts client-side while pre-rendering clean fallback HTML and metadata for instant SEO indexing.

How does ExodeUI scroll performance compare to GSAP ScrollTrigger?

GSAP modifies DOM element styles and CSS transforms on every scroll tick. ExodeUI updates a single GPU uniform register, entirely bypassing DOM recalculation and CSS style recalcs.

Can I scrub timelines backwards when scrolling up in Vue?

Yes. ExodeUI timelines are bidirectional. Normalizing scroll progress between 0.0 and 1.0 allows your scene to play forward and backward with identical precision.

Explore Related Vue Recipes & Documentation Hubs