You need to enable JavaScript to run this app. 如何修改Angular Material CdkDroplist行为实现自由拖拽放置区
优惠活动
大模型
产品
解决方案
定价
更多

如何修改Angular Material CdkDroplist行为实现自由拖拽放置区

解决方案

核心思路是区分自由放置区和列表类放置区的行为,通过CDK自带属性禁用自由放置区的自动排序,同时分离两种场景的事件处理逻辑:

1. 修改模板逻辑(app.component.html)

  • 给全宽自由放置区的cdkDropList添加[cdkDropListSortingDisabled]="true"属性,禁用内部自动排序,避免拖拽元素被自动对齐到列表序列
  • 自由放置区内的拖拽元素移除自定义的ngStyle transform定位,改用CDK原生的[cdkDragFreeDragPosition]绑定坐标,避免和CDK拖拽逻辑冲突
    修改后的核心代码:
<!-- 全宽自由放置区 -->
<div
  cdkDropList
  id="todo"
  [cdkDropListData]="todo"
  [cdkDropListConnectedTo]="widgetIds"
  [cdkDropListSortingDisabled]="true" <!-- 新增:禁用自由放置区排序 -->
  class="example-list board"
  (cdkDropListDropped)="drop($event)"
>
  <div class="example-box widget item{{item.id}}"
    id="item{{item.id}}" 
    *ngFor="let item of todo" 
    cdkDrag
    [cdkDragData]="item"
    [cdkDragFreeDragPosition]="item.dragPosition" <!-- 新增:绑定自由拖拽坐标 -->
  >{{item.label}}</div>
</div>

<!-- 下方两个列表类组件保持原有代码不变,保留排序特性 -->

2. 修改事件处理逻辑(app.component.ts)

  • 移除原有的cdkDragReleased事件和手动解析transform的逻辑,改用CDK原生getFreeDragPosition()方法获取坐标,避免坐标计算偏差
  • 在drop事件中区分放置容器类型,自由放置区仅更新坐标,不操作数组序列;列表类放置区保留原有排序、转移逻辑
    修改后的核心代码:
import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';

export class AppComponent {
  widgetIds = ['todo', 'inprogress', 'done'];

  todo = [
    { id: 1, label: 'Item 1', dragPosition: { x: 45, y: 549, z: 0 } },
    { id: 2, label: 'Item 2', dragPosition: { x: 190, y: 236, z: 0 } },
    { id: 5, label: 'Item 5', dragPosition: { x: 93, y: 142, z: 0 } }
  ];

  inprogress = [
    { id: 3, label: 'Item 3', dragPosition: { x: 0, y: 0, z: 0 } },
    { id: 6, label: 'Item 6', dragPosition: { x: 0, y: 0, z: 0 } },
    { id: 7, label: 'Item 7', dragPosition: { x: 0, y: 0, z: 0 } }
  ];

  done = [
    { id: 4, label: 'Item 4', dragPosition: { x: 0, y: 0, z: 0 } },
    { id: 8, label: 'Item 8', dragPosition: { x: 0, y: 0, z: 0 } }
  ];

  drop(event: CdkDragDrop<any>) {
    // 放置到自由放置区:仅更新坐标,不修改数组
    if (event.container.id === 'todo') {
      const dragPos = event.item.getFreeDragPosition();
      event.item.data.dragPosition = { x: dragPos.x, y: dragPos.y, z: 0 };
      return;
    }

    // 放置到列表类容器:保留原有排序、转移逻辑
    if (event.previousContainer === event.container) {
      moveItemInArray(
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      );
      // 元素从自由放置区移入列表,清空坐标避免后续拖回时位置异常
      event.item.data.dragPosition = {x: 0, y:0, z:0};
    }
  }
}

注意事项

  • 列表类放置区不要添加cdkDropListSortingDisabled属性,保持原有排序特性
  • 元素从自由放置区移入列表时清空坐标,避免后续拖回自由放置区时出现位置偏移
  • 该方案完全保留CDK原生拖拽的所有特性,不需要额外引入第三方依赖

内容的提问来源于stack exchange,提问作者Rafael de Castro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 16:15:07