Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce Vue 3 best practices—Composition API, script setup, TypeScript, component boundaries, and reactivity patterns
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/animation-class-based-technique.md
1---2title: Use Class-based Animations for Non-Enter/Leave Effects3impact: LOW4impactDescription: Class-based animations are simpler and more performant for elements that remain in the DOM5type: best-practice6tags: [vue3, animation, css, class-binding, state]7---89# Use Class-based Animations for Non-Enter/Leave Effects1011**Impact: LOW** - For animations on elements that are not entering or leaving the DOM, use CSS class-based animations triggered by Vue's reactive state. This is simpler than `<Transition>` and more appropriate for feedback animations like shake, pulse, or highlight effects.1213## Task List1415- Use class-based animations for elements staying in the DOM16- Use `<Transition>` only for enter/leave animations17- Combine CSS animations with Vue's class bindings (`:class`)18- Consider using `setTimeout` to auto-remove animation classes1920**When to Use Class-based Animations:**21- User feedback (shake on error, pulse on success)22- Attention-grabbing effects (highlight changes)23- Hover/focus states that need more than CSS transitions24- Any animation where the element stays mounted2526**When to Use Transition Component:**27- Elements entering/leaving the DOM (v-if/v-show)28- Route transitions29- List item additions/removals3031## Basic Pattern3233```vue34<template>35<div :class="{ shake: showError }">36<button @click="submitForm">Submit</button>37<span v-if="showError">This feature is disabled!</span>38</div>39</template>4041<script setup>42import { ref } from 'vue'4344const showError = ref(false)4546function submitForm() {47if (!isValid()) {48// Trigger shake animation49showError.value = true5051// Auto-remove class after animation completes52setTimeout(() => {53showError.value = false54}, 820) // Match animation duration55}56}57</script>5859<style>60.shake {61animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;62transform: translate3d(0, 0, 0); /* Enable GPU acceleration */63}6465@keyframes shake {6610%, 90% { transform: translate3d(-1px, 0, 0); }6720%, 80% { transform: translate3d(2px, 0, 0); }6830%, 50%, 70% { transform: translate3d(-4px, 0, 0); }6940%, 60% { transform: translate3d(4px, 0, 0); }70}71</style>72```7374## Common Animation Patterns7576### Pulse on Success7778```vue79<template>80<button81@click="save"82:class="{ pulse: saved }"83>84{{ saved ? 'Saved!' : 'Save' }}85</button>86</template>8788<script setup>89import { ref } from 'vue'9091const saved = ref(false)9293async function save() {94await saveData()95saved.value = true96setTimeout(() => saved.value = false, 1000)97}98</script>99100<style>101.pulse {102animation: pulse 0.5s ease-in-out;103}104105@keyframes pulse {1060%, 100% { transform: scale(1); }10750% { transform: scale(1.05); }108}109</style>110```111112### Highlight on Change113114```vue115<template>116<div117:class="{ highlight: justUpdated }"118>119Value: {{ value }}120</div>121</template>122123<script setup>124import { ref, watch } from 'vue'125126const value = ref(0)127const justUpdated = ref(false)128129watch(value, () => {130justUpdated.value = true131setTimeout(() => justUpdated.value = false, 1000)132})133</script>134135<style>136.highlight {137animation: highlight 1s ease-out;138}139140@keyframes highlight {1410% { background-color: yellow; }142100% { background-color: transparent; }143}144</style>145```146147### Bounce Attention148149```vue150<template>151<div152:class="{ bounce: needsAttention }"153@animationend="needsAttention = false"154>155<BellIcon />156</div>157</template>158159<script setup>160import { ref } from 'vue'161162const needsAttention = ref(false)163164function notifyUser() {165needsAttention.value = true166// No setTimeout needed - using animationend event167}168</script>169170<style>171.bounce {172animation: bounce 0.5s ease;173}174175@keyframes bounce {1760%, 100% { transform: translateY(0); }17750% { transform: translateY(-10px); }178}179</style>180```181182## Using animationend Event183184Instead of `setTimeout`, use the `animationend` event for cleaner code:185186```vue187<template>188<div189:class="{ animate: isAnimating }"190@animationend="isAnimating = false"191>192Content193</div>194</template>195196<script setup>197import { ref } from 'vue'198199const isAnimating = ref(false)200201function triggerAnimation() {202isAnimating.value = true203// Class is automatically removed when animation ends204}205</script>206```207208## Composable for Reusable Animations209210```javascript211// composables/useAnimation.js212import { ref } from 'vue'213214export function useAnimation(duration = 500) {215const isAnimating = ref(false)216217function trigger() {218isAnimating.value = true219setTimeout(() => {220isAnimating.value = false221}, duration)222}223224return {225isAnimating,226trigger227}228}229```230231```vue232<script setup>233import { useAnimation } from '@/composables/useAnimation'234235const shake = useAnimation(820)236const pulse = useAnimation(500)237</script>238239<template>240<button241:class="{ shake: shake.isAnimating.value }"242@click="shake.trigger()"243>244Shake me245</button>246247<button248:class="{ pulse: pulse.isAnimating.value }"249@click="pulse.trigger()"250>251Pulse me252</button>253</template>254```255