元素移动后如何重复触发动画?HTML/CSS/JS实现疑问
问题描述
我想要实现一个点击后移动并停留在新位置的盒子,鼠标悬浮时显示“愤怒”动画,点击后执行逃跑动画。但遇到两个问题:
- 盒子移动后无法重复触发动画,尝试通过添加/移除类的方式触发时,第二次操作会重置盒子位置
- 悬浮动画无法重播
我刚接触HTML/CSS和JS,不确定当前方案是否可行。
现有代码
HTML代码
<button type="button" id="box" class="hovering" onclick="runAway()">DONT CLICK ME!</button>
CSS代码
body{ background-color: slategrey; display: flex; flex-direction: column; } #box{ height: 100px; width: 100px; background-color: red; border: solid 3px black; position: absolute; top: 50%; left: 50%; margin: -50px 0 0 -50px; font-family:fantasy; display: table-cell; text-align: center; vertical-align: middle; } .hovering:hover{ font-weight: bold; animation: angry infinite .1s; } .hovering:active { box-shadow: inset -2px -2px 3px rgba(255, 255, 255, 0.6), inset 2px 2px 3px rgba(0, 0, 0, 0.6); } .running{ animation: runner 1s forwards; } @keyframes angry { 0% {transform: rotateZ(-3deg)} 50%{transform: rotateZ(3deg)} 100% {transform: rotateZ(-3deg)} } @keyframes runner { 100% {transform: translateX(100%);} }
JS代码
function runAway() { let theButton = document.getElementById('box'); theButton.classList.remove("hovering"); theButton.classList.add("running"); //how to repeat? simply adding and removing again doesnt work }
解决方案
问题核心在于:CSS动画的transform会覆盖元素原有位置,且直接移除类会丢失动画后的状态;同时移除hovering类会导致后续无法触发悬浮动画。以下是具体修改方案:
修改后的JS代码
function runAway() { const theButton = document.getElementById('box'); // 暂时移除hovering类,避免动画冲突 theButton.classList.remove("hovering"); // 先移除running类再强制重绘,确保动画能重新触发 theButton.classList.remove("running"); void theButton.offsetWidth; // 强制浏览器重绘元素 theButton.classList.add("running"); // 监听动画结束事件,只执行一次 theButton.addEventListener('animationend', function handleEnd() { // 保存动画结束后的最终位置到元素样式中 theButton.style.transform = window.getComputedStyle(theButton).transform; // 移除running类 theButton.classList.remove("running"); // 重新添加hovering类,恢复悬浮动画 theButton.classList.add("hovering"); // 移除事件监听,防止重复绑定 theButton.removeEventListener('animationend', handleEnd); }, { once: true }); }
可选CSS优化
把逃跑动画的目标位置改成屏幕右侧边缘,避免盒子移出视野:
@keyframes runner { 100% {transform: translateX(calc(100vw - 100px));} }
修改说明
- 强制重绘:
void theButton.offsetWidth让浏览器重新计算元素布局,确保重新添加running类时动画能从头播放 - 保存最终位置:通过
getComputedStyle获取动画结束后的transform值,赋值给元素的style.transform,永久保留位置,不会因为移除类重置 - 恢复悬浮动画:动画结束后重新添加
hovering类,让后续鼠标悬浮仍能触发愤怒动画 - 单次事件监听:
{ once: true }确保事件只执行一次,避免多次点击导致的重复绑定问题
内容的提问来源于stack exchange,提问作者big cat
相关产品推荐
相关产品推荐

