如何在Vue.js中多次切换元素类实现骰子滚动动画
Got it, let's get that dice roll animation working in your Vue component! The problem boils down to three key missing pieces: you don't have the CSS that powers the 3D roll effect, you're trying to access DOM elements too early, and your current rollDice method only switches classes once instead of simulating a rolling sequence. Here's how to fix it step by step:
1. Add the Critical CSS for Animation
The original demo relies heavily on CSS 3D transforms and transitions to create the roll effect. Add this to your component's <style> section (you can keep scoped or remove it if you run into style specificity issues):
.container { padding-top: 3rem; } .card { border: none; border-radius: 1rem; box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); } .card-header { background-color: #fff; border-bottom: none; border-top-left-radius: 1rem; border-top-right-radius: 1rem; display: flex; align-items: center; justify-content: space-between; } .blink { animation: blink 1s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } .dice { display: flex; justify-content: center; gap: 2rem; padding: 2rem 0; } .die-list { list-style: none; padding: 0; margin: 0; width: 6rem; height: 6rem; position: relative; transform-style: preserve-3d; transition: transform 0.6s ease; } .die-item { position: absolute; width: 100%; height: 100%; background-color: white; border-radius: 0.5rem; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0.25rem rgba(0,0,0,0.2); } .dot { width: 1rem; height: 1rem; background-color: #333; border-radius: 50%; margin: 0.25rem; } /* Die face layouts */ .die-item[data-side="1"] .dot { margin: 0; } .die-item[data-side="2"] { justify-content: space-between; } .die-item[data-side="2"] .dot:first-child { align-self: flex-start; } .die-item[data-side="2"] .dot:last-child { align-self: flex-end; } .die-item[data-side="3"] { justify-content: space-between; } .die-item[data-side="3"] .dot:first-child { align-self: flex-start; } .die-item[data-side="3"] .dot:nth-child(2) { align-self: center; } .die-item[data-side="3"] .dot:last-child { align-self: flex-end; } .die-item[data-side="4"], .die-item[data-side="5"], .die-item[data-side="6"] { flex-wrap: wrap; padding: 0.5rem; } .die-item[data-side="4"] .dot { width: 45%; height: 45%; } .die-item[data-side="5"] .dot:nth-child(3) { width: 100%; height: 100%; margin: 0 auto; } .die-item[data-side="6"] .dot { width: 45%; height: 30%; } /* Roll animations */ .even-roll[data-roll="1"] { transform: rotateX(0deg) rotateY(0deg); } .even-roll[data-roll="2"] { transform: rotateX(-90deg) rotateY(0deg); } .even-roll[data-roll="3"] { transform: rotateX(0deg) rotateY(90deg); } .even-roll[data-roll="4"] { transform: rotateX(0deg) rotateY(-90deg); } .even-roll[data-roll="5"] { transform: rotateX(90deg) rotateY(0deg); } .even-roll[data-roll="6"] { transform: rotateX(180deg) rotateY(0deg); } .odd-roll[data-roll="1"] { transform: rotateX(0deg) rotateY(0deg); } .odd-roll[data-roll="2"] { transform: rotateX(90deg) rotateY(0deg); } .odd-roll[data-roll="3"] { transform: rotateX(0deg) rotateY(-90deg); } .odd-roll[data-roll="4"] { transform: rotateX(0deg) rotateY(90deg); } .odd-roll[data-roll="5"] { transform: rotateX(-90deg) rotateY(0deg); } .odd-roll[data-roll="6"] { transform: rotateX(180deg) rotateY(0deg); }
2. Use Vue Refs Instead of DOM Queries
In Vue, it's better to use ref to access DOM elements instead of document.querySelectorAll. Update your template to add refs to the dice lists:
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-md-6"> <div class="card"> <div class="card-header"> Dice Game <h5 class="blink" style="color: red">* live</h5> </div> <div class="card-body" style="background-color: #3f0d12;background-image: linear-gradient(315deg, #3f0d12 0%, #a71d31 74%); "> <div class="dice"> <ol class="die-list even-roll" data-roll="1" ref="dieList"> <!-- Die 1 faces --> <li class="die-item" data-side="1"> <span class="dot"></span> </li> <li class="die-item" data-side="2"> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="3"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="4"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="5"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="6"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> </ol> <ol class="die-list odd-roll" data-roll="1" ref="dieList"> <!-- Die 2 faces --> <li class="die-item" data-side="1"> <span class="dot"></span> </li> <li class="die-item" data-side="2"> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="3"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="4"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="5"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> <li class="die-item" data-side="6"> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> <span class="dot"></span> </li> </ol> </div> </div> </div> </div> </div> </div> </template>
3. Update the Script to Simulate Rolling Animation
Modify your script to use refs, trigger the animation sequence properly, and wait for the DOM to be ready (use mounted instead of beforeMount):
<script> export default { name: "Home", methods: { async rollDice() { const dice = this.$refs.dieList; if (!dice.length) return; // Simulate rolling for 1 second with rapid class/value changes const rollInterval = setInterval(() => { dice.forEach(die => { this.toggleClasses(die); die.dataset.roll = this.getRandomNumber(1, 6); }); }, 100); // Stop rolling and set final random values setTimeout(() => { clearInterval(rollInterval); dice.forEach(die => { this.toggleClasses(die); die.dataset.roll = this.getRandomNumber(1, 6); }); }, 1000); }, toggleClasses(die) { die.classList.toggle("odd-roll"); die.classList.toggle("even-roll"); }, getRandomNumber(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; }, }, mounted() { this.rollDice(); }, } </script>
Key Changes Explained
- CSS Added: The CSS provides the 3D transform rules, face layouts, and transition effects that make the dice roll look realistic.
- Refs Used:
this.$refs.dieListsafely accesses the dice elements after the DOM is fully rendered, avoiding early DOM query issues. - Animation Sequence: The
rollDicemethod usessetIntervalto rapidly switch classes and random values for 1 second, creating the rolling effect, then stops on final random numbers. - Mounted Lifecycle:
mountedensures the DOM is ready before trying to interact with elements, which fixes the issue of querying elements too early.
Now your dice should roll smoothly when the component loads! You can also add a button to trigger rollDice on click if you want user-initiated rolls.
内容的提问来源于stack exchange,提问作者Vansh Tah

