如何修改Angular Material CdkDroplist行为实现自由拖拽放置区
解决方案
核心思路是区分自由放置区和列表类放置区的行为,通过CDK自带属性禁用自由放置区的自动排序,同时分离两种场景的事件处理逻辑:
1. 修改模板逻辑(app.component.html)
- 给全宽自由放置区的
cdkDropList添加[cdkDropListSortingDisabled]="true"属性,禁用内部自动排序,避免拖拽元素被自动对齐到列表序列 - 自由放置区内的拖拽元素移除自定义的
ngStyletransform定位,改用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
相关产品推荐
相关产品推荐

