Animation Controls
Déjà Vue provides reactive props (progress and toggle) to control your animations dynamically based on component state.
Overview
Two main control mechanisms:
progress- Scrub through an animation (0-1)toggle- Play/reverse the animation
Progress Control
Use the progress prop to scrub through an animation:
vue
<template>
<div>
<input
v-model.number="progress"
type="range"
min="0"
max="1"
step="0.01"
>
<div>Progress: {{ (progress * 100).toFixed(0) }}%</div>
<Tween
method="to"
:progress
:vars="{ x: 200, duration: 2 }"
>
<div>Controlled Animation</div>
</Tween>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tween } from 'deja-vue'
const progress = ref(0.5)
</script>When progress is set:
- The animation is paused at that progress point
- Values are continuous (0.0 to 1.0)
- Perfect for scrubbing/scrubber UI
- Animation snaps to the exact frame instantly
Toggle Control
Use the toggle prop to play or reverse animations:
vue
<template>
<div>
<button @click="isPlaying = !isPlaying">
{{ isPlaying ? '⏸ Pause' : '▶ Play' }}
</button>
<Tween
method="to"
:toggle="isPlaying"
:vars="{ rotation: 360, duration: 3, repeat: -1 }"
>
<div class="spinner">Spinning</div>
</Tween>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tween } from 'deja-vue'
const isPlaying = ref(false)
</script>When toggle is provided:
true= play animationfalse= reverse animation- Perfect for play/pause buttons
undefined= default behavior (auto-play)
Combining Controls
You can use both progress and toggle together:
vue
<template>
<div>
<div class="controls">
<button @click="playing = !playing">
{{ playing ? '⏸ Pause' : '▶ Play' }}
</button>
<input
v-model.number="progress"
type="range"
min="0"
max="1"
step="0.01"
>
<span>{{ (progress * 100).toFixed(0) }}%</span>
</div>
<Timeline
:tweens
:progress
:toggle="playing"
>
<div class="content">Complex Animation</div>
</Timeline>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Timeline } from 'deja-vue'
const playing = ref(false)
const progress = ref(0)
const tweens = [
{ method: 'to', vars: { x: 100, duration: 1 } },
{ method: 'to', vars: { y: 100, duration: 1 }, position: '+=0.5' },
{ method: 'to', vars: { rotation: 360, duration: 1 }, position: '+=0.5' }
]
</script>
<style scoped>
.controls {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 20px;
}
.content {
width: 100px;
height: 100px;
background: blue;
}
</style>Reactive Updates
Controls are fully reactive and work with computed properties and watchers:
vue
<template>
<div>
<input v-model.number="position" type="range" min="0" max="1" step="0.0001">
<Tween
method="to"
:progress
:vars="{ x: 300, duration: 1 }"
>
<div>Moving Element</div>
</Tween>
</div>
</template>
<script setup>
import { computed, ref } from 'vue'
import { Tween } from 'deja-vue'
const progress = ref(0)
</script>Programmatic Control
Access the animation instance for direct GSAP timeline control:
vue
<template>
<div>
<button @click="play">Play</button>
<button @click="pause">Pause</button>
<button @click="reverse">Reverse</button>
<button @click="seek(0.5)">Seek to 50%</button>
<Tween
ref="tweenRef"
method="to"
:vars="{ x: 100, duration: 1 }"
>
<div>Programmatic Control</div>
</Tween>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tween } from 'deja-vue'
const tweenRef = ref()
const play = () => tweenRef.value.animation.timeline.play()
const pause = () => tweenRef.value.animation.timeline.pause()
const reverse = () => tweenRef.value.animation.timeline.reverse()
const seek = (progress) => tweenRef.value.animation.timeline.progress(progress)
</script>Common Patterns
Video Player Style Scrubber
vue
<template>
<div class="video-player">
<div class="preview">
<Timeline :tweens="frameTweens" :progress="currentFrame / totalFrames">
<div class="frame" />
</Timeline>
</div>
<input
v-model.number="currentFrame"
type="range"
:max="totalFrames"
@input="isPlaying = false"
>
<button @click="isPlaying = !isPlaying">
{{ isPlaying ? 'Pause' : 'Play' }}
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Timeline } from 'deja-vue'
const isPlaying = ref(false)
const currentFrame = ref(0)
const totalFrames = ref(100)
const frameTweens = [
{ method: 'to', vars: { rotation: 360, duration: 1 } }
]
// Auto-playback
setInterval(() => {
if (!isPlaying.value) return
currentFrame.value = (currentFrame.value + 1) % totalFrames.value
}, 16) // ~60fps
</script>Scroll-Triggered Animation
vue
<template>
<div class="scroll-container" @scroll="onScroll">
<Tween
method="to"
:vars="{ x: 200, duration: 1 }"
:progress="scrollProgress"
>
<div>Scroll to animate</div>
</Tween>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Tween } from 'deja-vue'
const scrollProgress = ref(0)
const onScroll = (e) => {
const { scrollLeft, scrollWidth, clientWidth } = e.target
scrollProgress.value = scrollLeft / (scrollWidth - clientWidth)
}
</script>Hover Animations
vue
<template>
<Tween
method="to"
:vars="{ scale: 1.1, duration: 0.3 }"
:toggle="isHovered"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
>
<div class="button">Hover me</div>
</Tween>
</template>
<script setup>
import { ref } from 'vue'
import { Tween } from 'deja-vue'
const isHovered = ref(false)
</script>