vue.jsanimationvuejs3uniapp

Why doesn't binding the animation-name dynamically work in Vue.js and Uni-App?


I'm working on a Vue 3 project with Uni-App, and I'm trying to bind the animation attribute of CSS dynamically.

I have successfully implemented the animation duration dynamically, but when I attempt to bind the animation-name dynamically, it doesn't seem to work.

Here’s the working version of the code where I bind the animation property with the animation-duration:

<template>
    <view class="box" :class="{ box__move: steps > 0 }"> </view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
const frameTime = 400 // 帧时间,单位ms
const steps = ref(0)
const animationDuration = computed(() => {
    return `${(steps.value * frameTime) / 1000}s`
})

setTimeout(() => {
    steps.value = 2
}, 2000)
</script>
<style scoped lang="scss">
.box {
    width: 100px;
    height: 100px;
    background-color: lightblue;
    &.box__move {
        animation: moveTo2Steps v-bind(animationDuration) ease forwards;
    }
}
@keyframes moveTo2Steps {
    0% {
        transform: translateX(10px) translateY(10px);
    }
    50% {
        transform: translateX(30px) translateY(30px);
    }
    75% {
        transform: translateX(40px) translateY(60px);
    }
    100% {
        transform: translateX(50px) translateY(120px);
    }
}
</style>

However, when I try to bind the animation-name dynamically, it stops working. Here is my modified code:

<template>
    <view class="box" :class="{ box__move: steps > 0 }"> </view>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
const frameTime = 400 // 帧时间,单位ms
const steps = ref(0)
const animationDuration = computed(() => {
    return `${(steps.value * frameTime) / 1000}s`
})

setTimeout(() => {
    steps.value = 2
}, 5000)

const animationName = computed(() => `moveTo${steps.value}Steps`)
</script>
<style scoped lang="scss">
.box {
    width: 100px;
    height: 100px;
    background-color: lightblue;
    &.box__move {
        animation: v-bind(animationName) v-bind(animationDuration) ease forwards;
    }
}
@keyframes moveTo2Steps {
    0% {
        transform: translateX(10px) translateY(10px);
    }
    50% {
        transform: translateX(30px) translateY(30px);
    }
    75% {
        transform: translateX(40px) translateY(60px);
    }
    100% {
        transform: translateX(50px) translateY(120px);
    }
}
</style>

Project Environment:

What I expect:

What’s happening:

What I’ve tried:

Can someone help me understand why this approach isn’t working and suggest a solution?


Solution

  • You're using <style scoped>, which, under the hood, makes the keyframes name become something like: @keyframes moveTo2Steps-v-6e332e59 { ... }.

    The value you're binding dynamically is the un-scoped @keyframes name: moveTo2Steps, which is not a defined @keyframes name at runtime.

    To make it work, move the @keyframes definition block into a separate, un-scoped <style> tag.
    You can have more than one <style> tag in the same SFC.

    See it working.