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

如何通过动画实现两个Div的位置与尺寸互换?

解决方案:实现大小Div平滑互换并支持DOM移动

核心思路

  • 用CSS transition 统一处理尺寸、位置的平滑过渡
  • 点击触发时先将两个元素转为绝对定位,脱离正常流以精准控制位置
  • 等待过渡动画完全结束后再交换DOM结构,避免动画中断
  • 最后重置定位与行内样式,让元素回到正常流维持布局

修正后的代码实现

HTML(保持原有结构即可)

<div class="container">
  <div class="full-size-card card">大卡片</div>
  <div class="inactive-sheets">
    <div class="card">小卡片1</div>
    <div class="card">小卡片2</div>
    <div class="card">小卡片3</div>
  </div>
</div>

CSS(关键修正与补充)

.container {
  position: relative; /* 作为绝对定位元素的参照容器 */
  padding: 20px;
}

.card {
  transition: all 0.3s ease; /* 统一过渡动画参数 */
  position: relative; /* 默认相对定位,维持正常流布局 */
  border: 1px solid #ccc;
  padding: 10px;
  cursor: pointer;
}

.full-size-card {
  width: 400px;
  height: 200px;
  margin-bottom: 20px;
}

.inactive-sheets {
  display: flex;
  gap: 10px;
}

.inactive-sheets .card {
  width: 120px;
  height: 80px;
}

/* 过渡阶段临时绝对定位类 */
.card-transition {
  position: absolute !important;
  margin: 0 !important;
}

jQuery 代码(修复定位逻辑)

$(function () {
  $('.inactive-sheets .card').on('click', function() {
    const $target = $(this);
    const $currentFull = $('.full-size-card');
    const $container = $('.container');
    
    // 1. 获取元素相对于容器的位置与尺寸(避免页面滚动影响)
    const containerRect = $container[0].getBoundingClientRect();
    const currentRect = $currentFull[0].getBoundingClientRect();
    const targetRect = $target[0].getBoundingClientRect();
    
    const currentPos = {
      left: currentRect.left - containerRect.left,
      top: currentRect.top - containerRect.top,
      width: currentRect.width,
      height: currentRect.height
    };
    const targetPos = {
      left: targetRect.left - containerRect.left,
      top: targetRect.top - containerRect.top,
      width: targetRect.width,
      height: targetRect.height
    };
    
    // 2. 给元素添加绝对定位,脱离正常流
    $currentFull.addClass('card-transition');
    $target.addClass('card-transition');
    
    // 3. 强制浏览器重绘,确保过渡动画触发
    void $currentFull[0].offsetWidth;
    
    // 4. 设置目标位置与尺寸,启动过渡
    $currentFull.css({
      left: targetPos.left,
      top: targetPos.top,
      width: targetPos.width,
      height: targetPos.height
    });
    $target.css({
      left: currentPos.left,
      top: currentPos.top,
      width: currentPos.width,
      height: currentPos.height
    });
    
    // 5. 过渡结束后交换DOM并重置样式
    $currentFull.one('transitionend', function() {
      // 交换DOM位置:大卡片移到小卡片容器,目标卡片移到顶部
      $currentFull.removeClass('full-size-card card-transition').appendTo('.inactive-sheets');
      $target.removeClass('card-transition').addClass('full-size-card').prependTo('.container');
      
      // 清除行内样式,让CSS规则重新生效
      $currentFull.add($target).removeAttr('style');
    });
  });
});

关键细节解释

  • 容器参照定位:基于容器的getBoundingClientRect计算相对偏移,解决页面滚动导致的位置计算错误
  • 强制重绘:void $currentFull[0].offsetWidth 触发浏览器重绘,避免CSS过渡直接跳转到目标状态
  • 过渡后DOM交换:等动画完全结束后再移动DOM元素,确保过渡效果完整展示;移动后清除行内样式,让CSS规则接管正常布局

原生JS替代方案

如果无需依赖jQuery,逻辑完全一致,仅API替换为原生:

document.addEventListener('DOMContentLoaded', function() {
  const container = document.querySelector('.container');
  const inactiveCards = document.querySelectorAll('.inactive-sheets .card');
  
  inactiveCards.forEach(card => {
    card.addEventListener('click', function() {
      const targetCard = this;
      const currentFull = document.querySelector('.full-size-card');
      
      const containerRect = container.getBoundingClientRect();
      const currentRect = currentFull.getBoundingClientRect();
      const targetRect = targetCard.getBoundingClientRect();
      
      const currentPos = {
        left: currentRect.left - containerRect.left,
        top: currentRect.top - containerRect.top,
        width: currentRect.width,
        height: currentRect.height
      };
      const targetPos = {
        left: targetRect.left - containerRect.left,
        top: targetRect.top - containerRect.top,
        width: targetRect.width,
        height: targetRect.height
      };
      
      currentFull.classList.add('card-transition');
      targetCard.classList.add('card-transition');
      
      void currentFull.offsetWidth;
      
      currentFull.style.left = `${targetPos.left}px`;
      currentFull.style.top = `${targetPos.top}px`;
      currentFull.style.width = `${targetPos.width}px`;
      currentFull.style.height = `${targetPos.height}px`;
      
      targetCard.style.left = `${currentPos.left}px`;
      targetCard.style.top = `${currentPos.top}px`;
      targetCard.style.width = `${currentPos.width}px`;
      targetCard.style.height = `${currentPos.height}px`;
      
      currentFull.addEventListener('transitionend', function handler() {
        currentFull.classList.remove('full-size-card', 'card-transition');
        targetCard.classList.remove('card-transition');
        targetCard.classList.add('full-size-card');
        
        document.querySelector('.inactive-sheets').appendChild(currentFull);
        container.prepend(targetCard);
        
        currentFull.removeAttribute('style');
        targetCard.removeAttribute('style');
        
        currentFull.removeEventListener('transitionend', handler);
      }, { once: true });
    });
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:25:32