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

基于CSS和jQuery实现类Vue <transition-group>的列表洗牌动画

实现类似Vue的列表洗牌动画(CSS + jQuery版本)

嗨,我明白你想实现那种Vue风格的平滑洗牌动画的痛点——直接移动DOM只会让元素跳变,完全没有过渡效果。其实Vue背后用的是FLIP动画技术(First, Last, Invert, Play),核心是先记录元素的初始位置,计算目标位置的偏移,用transform过渡这个偏移后再更新DOM,视觉上就会有平滑的动画。针对你提到的两个元素的场景,我给你写个具体的实现方案:

核心思路拆解

  1. First:记录每个元素在洗牌前的初始位置
  2. Last:模拟洗牌后的DOM结构,记录每个元素的目标位置
  3. Invert:计算初始位置到目标位置的偏移量
  4. Play:用CSS过渡让元素从初始位置平滑移动到目标位置,最后重置DOM结构和样式

完整代码实现

HTML结构

<div class="list-container">
  <div class="list-item">Item 1</div>
  <div class="list-item">Item 2</div>
</div>
<button id="shuffle-btn">Shuffle Items</button>

CSS样式

.list-container {
  position: relative;
  width: 200px;
  margin: 2rem;
  min-height: 80px; /* 避免容器高度跳变 */
}

.list-item {
  padding: 1rem;
  margin: 0.5rem 0;
  background: #f0f0f0;
  border-radius: 4px;
  width: 100%;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 用Vue常用的缓动曲线 */
  position: absolute; /* 脱离文档流,方便自由移动 */
  box-sizing: border-box;
}

jQuery逻辑

$('#shuffle-btn').click(function() {
  const $container = $('.list-container');
  const $items = $container.find('.list-item');
  const itemCount = $items.length;

  // 1. 记录每个元素的初始位置(相对于容器)
  const initialPositions = $items.map(function() {
    const itemRect = this.getBoundingClientRect();
    const containerRect = $container[0].getBoundingClientRect();
    return {
      top: itemRect.top - containerRect.top,
      left: itemRect.left - containerRect.left
    };
  }).get();

  // 2. 模拟洗牌后的顺序(这里直接交换两个元素)
  const shuffledOrder = $items.toArray().reverse();

  // 3. 先临时渲染洗牌后的结构,记录目标位置
  $container.empty();
  const targetPositions = [];
  shuffledOrder.forEach(item => {
    $container.append(item);
    const itemRect = item.getBoundingClientRect();
    const containerRect = $container[0].getBoundingClientRect();
    targetPositions.push({
      top: itemRect.top - containerRect.top,
      left: itemRect.left - containerRect.left
    });
  });

  // 4. 把元素放回初始位置,准备执行过渡
  $container.empty();
  $items.each(function(index) {
    const $item = $(this);
    // 设置初始定位
    $item.css({
      top: `${initialPositions[index].top}px`,
      left: `${initialPositions[index].left}px`
    });
    $container.append($item);

    // 强制浏览器重绘,否则过渡会直接跳到终点
    void $item[0].offsetWidth;

    // 计算偏移量,设置transform触发过渡
    const offsetTop = targetPositions[index].top - initialPositions[index].top;
    const offsetLeft = targetPositions[index].left - initialPositions[index].left;
    $item.css('transform', `translate(${offsetLeft}px, ${offsetTop}px)`);
  });

  // 5. 过渡完成后,重置样式,让元素回到正常文档流
  setTimeout(() => {
    $items.each(function() {
      $(this).css({
        position: 'static',
        transform: 'none',
        top: 'auto',
        left: 'auto'
      });
    });
  }, 300); // 这个时间要和CSS的transition-duration一致
});

关键细节说明

  • 绝对定位的必要性:让元素脱离文档流,这样移动时不会影响其他元素的布局,保证动画过程中容器高度稳定
  • 强制重绘:void $item[0].offsetWidth这一步是关键,它会迫使浏览器立即计算元素的布局,避免样式修改被合并,导致过渡不生效
  • FLIP的简化应用:我们没有直接操作DOM的实时变化,而是通过"先记录位置→过渡→再更新DOM"的方式,模拟了Vue内部的过渡逻辑
  • 缓动曲线:用了cubic-bezier(0.4, 0, 0.2, 1),这是Vue默认的过渡缓动,能让动画更自然

内容的提问来源于stack exchange,提问作者Стас Пишевский

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:42:07