JavaScript调用两次动画函数仅执行一次的问题求助
问题描述
我是编程新手,正在为学校作业开发一款游戏。用CSS实现了骰子摇晃后显示点数的动画,期望调用一次playYourTurn()函数(绑定到按钮),能先后执行两次动画并移动对应玩家。但目前仅第二个动画playSpino()运行,尝试过调整函数结构、修改setTimeout间隔均无效,偶尔仅第一个动画playSasha()运行。玩家标记的移动逻辑已通过日志验证正常,仅动画存在问题。
相关JavaScript代码
function playYourTurn() { playSasha(); window.setTimeout(() => { }, 1500); playSpino(); } function playSasha() { var diceResultSasha = rollDice(); window.setTimeout(() => { moveSasha(diceResultSasha) }, 1000) window.setTimeout(resetDice, 3000) } function playSpino() { var diceResultSpino = rollDice(); window.setTimeout(() => { moveSpino(diceResultSpino) }, 4000) window.setTimeout(resetDice, 7000) }
rollDice()函数
function rollDice() { resetDice(); var diceresult = document.getElementById('diceresult') var dice = Math.floor(Math.random() * 6) + 1; document.getElementById('shakebox').className = 'diceAnimation'; diceresult.className = 'resultVisible'; // 后续包含switch语句
CSS动画代码
.diceAnimation { animation: shake 0.2s; animation-iteration-count: 5; } @keyframes shake { 0% { transform: translate(1px, 1px) rotate(0deg); } 10% { transform: translate(-2px, -3px) rotate(-20deg); } 20% { transform: translate(-4px, 0px) rotate(20deg); } 30% { transform: translate(3px, 2px) rotate(0deg); } 40% { transform: translate(1px, -1px) rotate(10deg); } 50% { transform: translate(-1px, 2px) rotate(-10deg); } 60% { transform: translate(-3px, 1px) rotate(0deg); } 70% { transform: translate(3px, 1px) rotate(-20deg); } 80% { transform: translate(-1px, -1px) rotate(20deg); } 90% { transform: translate(1px, 2px) rotate(0deg); } 100% { transform: translate(1px, -2px) rotate(-10deg); } }
骰子点数显示CSS
#diceresult { opacity: 0; position: absolute; } .resultVisible { opacity: 1 !important; transition: all 1s ease; }
问题原因与解决方案
核心问题
- 异步逻辑失效:
playYourTurn()里的空setTimeout根本不会延迟playSpino()执行,两个play函数几乎同时调用,rollDice()中的resetDice()会立刻重置骰子状态,直接覆盖第一个动画。 - 时间线混乱:原代码中两个玩家的动画、重置时间完全重叠,互相干扰,导致只有后执行的动画能显示。
修复方案
方案1:用Promise+async/await控制顺序(推荐)
把每个玩家的回合封装成Promise,确保前一个动画完成后再执行下一个:
// 封装Sasha的回合为Promise function playSasha() { return new Promise(resolve => { const diceResult = rollDice(); setTimeout(() => moveSasha(diceResult), 1000); // 重置骰子后标记回合完成 setTimeout(() => { resetDice(); resolve(); }, 3000); }); } // 封装Spino的回合为Promise function playSpino() { return new Promise(resolve => { const diceResult = rollDice(); setTimeout(() => moveSpino(diceResult), 1000); setTimeout(() => { resetDice(); resolve(); }, 3000); }); } // 改造playYourTurn为异步函数 async function playYourTurn() { await playSasha(); // 等待Sasha的动画和移动完成 await new Promise(resolve => setTimeout(resolve, 500)); // 可选:两个回合之间加短暂间隔 await playSpino(); // 执行Spino的回合 }
方案2:用回调函数(新手友好)
通过回调函数,在第一个回合结束后触发第二个回合:
function playYourTurn() { // 传入回调,Sasha回合结束后执行Spino的回合 playSasha(() => playSpino()); } // 修改playSasha,添加回调参数 function playSasha(callback) { const diceResult = rollDice(); setTimeout(() => moveSasha(diceResult), 1000); setTimeout(() => { resetDice(); if (callback) callback(); // 执行回调,触发下一个回合 }, 3000); } // 调整playSpino的时间线,和Sasha保持一致 function playSpino() { const diceResult = rollDice(); setTimeout(() => moveSpino(diceResult), 1000); setTimeout(resetDice, 3000); }
额外优化:确保动画能重复触发
CSS动画仅在类添加时执行一次,所以在rollDice()中需要先移除类再重新添加,强制触发动画:
function rollDice() { resetDice(); const shakebox = document.getElementById('shakebox'); // 先移除类,强制浏览器重绘后再添加 shakebox.classList.remove('diceAnimation'); void shakebox.offsetWidth; // 触发重绘 shakebox.classList.add('diceAnimation'); const diceresult = document.getElementById('diceresult'); const dice = Math.floor(Math.random() * 6) + 1; diceresult.className = 'resultVisible'; // 后续switch语句设置点数... }
内容的提问来源于stack exchange,提问作者user30513672
相关产品推荐
相关产品推荐

