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

带Transition的矩形交换后出现多余位移,如何消除该异常?

修复矩形交换动画后的多余移动问题

问题根源

你遇到的多余移动,是因为transform仅做视觉偏移,DOM实际位置在动画过程中并未改变。用setTimeout固定时间后交换DOM时,元素布局位置突然变化,此时移除transform类,浏览器会自动计算从动画偏移位置到新布局位置的过渡,导致出现“额外移动”。另外setTimeout的时间和transition时长不一定完全匹配,也会加剧这个问题。

修复方案

用transitionend事件替代setTimeout监听动画完成,并且在DOM交换后强制浏览器重绘,再移除动画类,避免触发不必要的过渡。

修改后的代码

JavaScript

const rectangles = document.getElementsByClassName("rectangle");

for (let i = 0; i < rectangles.length; i++) {
  const rectangle = rectangles[i];
  const upButton = rectangle.querySelector(".up");
  const downButton = rectangle.querySelector(".down");

  upButton.addEventListener("click", () => moveFile(rectangle, "up"));
  downButton.addEventListener("click", () => moveFile(rectangle, "down"));
}

function moveFile(currentRectangle, direction) {
  const siblingRectangle = direction === "up" 
    ? currentRectangle.previousElementSibling 
    : currentRectangle.nextElementSibling;

  if (!siblingRectangle) return;

  // 添加动画类
  const currentClass = direction === "up" ? "move-up" : "move-down";
  const siblingClass = direction === "up" ? "move-down" : "move-up";
  
  currentRectangle.classList.add(currentClass);
  siblingRectangle.classList.add(siblingClass);

  // 监听动画结束事件(只执行一次)
  const handleTransitionEnd = () => {
    // 交换DOM位置
    if (direction === "up") {
      currentRectangle.parentNode.insertBefore(currentRectangle, siblingRectangle);
    } else {
      currentRectangle.parentNode.insertBefore(siblingRectangle, currentRectangle);
    }

    // 强制浏览器重绘,确保DOM交换后的布局生效
    void currentRectangle.offsetHeight;
    void siblingRectangle.offsetHeight;

    // 移除动画类
    currentRectangle.classList.remove(currentClass);
    siblingRectangle.classList.remove(siblingClass);

    // 清理事件监听,避免重复触发
    currentRectangle.removeEventListener("transitionend", handleTransitionEnd);
    siblingRectangle.removeEventListener("transitionend", handleTransitionEnd);
  };

  currentRectangle.addEventListener("transitionend", handleTransitionEnd);
  siblingRectangle.addEventListener("transitionend", handleTransitionEnd);
}

CSS(保留原样式)

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  height: 400px;
}

.rectangle {
  width: 150px;
  height: 100px;
  background-color: #ccc;
  border: solid rgb(255, 126, 126);
  margin: 10px;
  text-align: center;
  font-size: 18px;
  position: relative;
  transition: transform 0.5s ease-in-out;
}

.move-up {
  transform: translateY(-120px);
}

.move-down {
  transform: translateY(120px);
}

.button {
  width: 100px;
  height: 30px;
  background-color: #ddd;
  border: none;
  margin: 5px;
  cursor: pointer;
}

HTML(保留原结构)

<div class="container">
  <div class="rectangle">
    <div>1</div>
    <button class="button up">Up</button>
    <button class="button down">Down</button>
  </div>
  <div class="rectangle">
    <div>2</div>
    <button class="button up">Up</button>
    <button class="button down">Down</button>
  </div>
  <div class="rectangle">
    <div>3</div>
    <button class="button up">Up</button>
    <button class="button down">Down</button>
  </div>
</div>

关键改动说明

  1. 用transitionend替代setTimeout:确保动画真正完成后再操作DOM,避免时间不匹配导致的提前或延迟操作。
  2. 强制重绘:通过void element.offsetHeight触发浏览器重绘,让浏览器先更新DOM交换后的布局位置,再移除transform类,此时transform从偏移值回到0,不会触发额外过渡(因为布局位置已经和目标位置一致)。
  3. 清理事件监听:动画结束后移除transitionend监听,避免后续操作重复触发。

内容的提问来源于stack exchange,提问作者First Last

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:04:57