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

Angular 18中CDK Drag&Drop手动调整项顺序(上下按钮)时添加过渡动画的实现方案

Angular 18中CDK Drag&Drop手动调整项顺序(上下按钮)时添加过渡动画的实现方案

我完全懂你现在的困扰——用CDK拖拽调整电影列表时,动画流畅自然,但点击上下按钮手动移动项的时候,元素唰的一下就换位置了,完全没过渡,体验感一下子就下来了。别担心,咱们可以结合Angular的动画能力,给手动移动也配上和拖拽一致的顺滑动画,让两种操作的体验统一起来。

第一步:准备动画基础配置

首先,要确保你的Angular 18独立组件项目支持动画,在main.ts的应用配置里添加动画提供者:

import { bootstrapApplication } from '@angular/platform-browser';
import { provideAnimations } from '@angular/platform-browser/animations';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [provideAnimations()]
});

第二步:修改组件逻辑与添加动画触发器

接下来更新你的拖拽组件,导入动画相关模块,定义列表项的动画触发器,同时在手动移动的方法里触发和拖拽一致的动画效果:

import {Component} from '@angular/core';
import {
  CdkDrag,
  CdkDragDrop,
  CdkDragPlaceholder,
  CdkDropList,
  moveItemInArray,
} from '@angular/cdk/drag-drop';
import { trigger, transition, style, animate, query } from '@angular/animations';

// 定义列表变化时的动画触发器,监听DOM重排过程
const listItemAnimation = trigger('listItemAnimation', [
  transition('* <=> *', [
    query(':not(:enter, :leave)', [
      style({ transform: 'translateY(0)' }),
      animate('300ms cubic-bezier(0.35, 0, 0.25, 1)', style({ transform: 'translateY(0)' }))
    ], { optional: true })
  ])
]);

@Component({
  standalone: true,
  selector: 'cdk-drag-drop-custom-placeholder-example',
  template: `<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)" @listItemAnimation>
  @for (movie of movies; track movie; let i = $index) {
    <button (click)="moveUp(movie)">Up</button>
    <button (click)="moveDown(movie)">Down</button>
    <div class="example-box" cdkDrag [attr.data-movie]="movie">
      <div class="example-custom-placeholder" *cdkDragPlaceholder></div>
      {{movie}}
    </div>
  }
</div>`,
  styleUrl: 'cdk-drag-drop-custom-placeholder-example.css',
  imports: [CdkDropList, CdkDrag, CdkDragPlaceholder],
  animations: [listItemAnimation] // 注册动画到组件
})
export class CdkDragDropCustomPlaceholderExampleComponent {
  movies = [
    'Episode I - The Phantom Menace',
    'Episode II - Attack of the Clones',
    'Episode III - Revenge of the Sith',
    'Episode IV - A New Hope',
    'Episode V - The Empire Strikes Back',
  ];

  moveUp(item: string) {
    const index = this.movies.indexOf(item);
    if (index === 0) return; // 已经是第一个,无需操作

    // 通过自定义属性定位到目标元素
    const element = document.querySelector(`.example-box[data-movie="${item}"]`);
    if (element) {
      // 添加拖拽动画类,模拟拖拽时的视觉效果
      element.classList.add('cdk-drag-dragging');
    }

    // 调整数组顺序
    moveItemInArray(this.movies, index, index - 1);

    // 动画结束后移除类,恢复正常状态
    setTimeout(() => {
      element?.classList.remove('cdk-drag-dragging');
    }, 300); // 时长和CSS过渡保持一致
  }

  moveDown(item: string) {
    const index = this.movies.indexOf(item);
    if (index === this.movies.length - 1) return; // 已经是最后一个,无需操作

    const element = document.querySelector(`.example-box[data-movie="${item}"]`);
    if (element) {
      element.classList.add('cdk-drag-dragging');
    }

    moveItemInArray(this.movies, index, index + 1);

    setTimeout(() => {
      element?.classList.remove('cdk-drag-dragging');
    }, 300);
  }

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
  }
}

第三步:增强CSS过渡效果

最后修改CSS文件,让动画效果更明显,和拖拽操作的视觉反馈保持统一:

.example-list {
  width: 500px;
  max-width: 100%;
  border: solid 1px #ccc;
  min-height: 60px;
  display: block;
  background: white;
  border-radius: 4px;
  overflow: hidden;
}

.example-box {
  padding: 20px 10px;
  border-bottom: solid 1px #ccc;
  color: rgba(0, 0, 0, 0.87);
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-sizing: border-box;
  cursor: move;
  background: white;
  font-size: 14px;
  /* 给列表项添加基础位置过渡 */
  transition: transform 0.3s cubic-bezier(0.35, 0, 0.25, 1);
}

.example-box:last-child {
  border: none;
}

.example-custom-placeholder {
  background: #ccc;
  border: dotted 3px #999;
  min-height: 60px;
  transition: transform 0.3s ease;
}

/* 手动移动时的动画效果,和拖拽状态保持一致 */
.cdk-drag-dragging {
  transform: scale(1.05);
  box-shadow: 0 5px 15px rgba(0,0,0,0.2);
  transition: all 0.3s cubic-bezier(0.35, 0, 0.25, 1);
}

这样修改后,不管是拖拽还是点击上下按钮,列表项的移动都会有流畅的缩放+位置过渡动画,两种操作的体验就完全统一啦!

备注:内容来源于stack exchange,提问作者Naveh Elya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 10:24:51