You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

元素移动后如何重复触发动画?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));}    
}

修改说明

  1. 强制重绘:void theButton.offsetWidth让浏览器重新计算元素布局,确保重新添加running类时动画能从头播放
  2. 保存最终位置:通过getComputedStyle获取动画结束后的transform值,赋值给元素的style.transform,永久保留位置,不会因为移除类重置
  3. 恢复悬浮动画:动画结束后重新添加hovering类,让后续鼠标悬浮仍能触发愤怒动画
  4. 单次事件监听:{ once: true }确保事件只执行一次,避免多次点击导致的重复绑定问题

内容的提问来源于stack exchange,提问作者big cat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 10:22:52