Angualr drag-drop里面的功能能让我们非常方便的处理页面上视图的拖拽(自由拖拽、列表排序拖拽、列表之间拖拽)问题。推荐大伙儿直接看官网,因为下面的内容绝大部分内容包括例子都来源于官网 https://material.angular.io/cdk/drag-drop/overview。一直认为官网才是最好的文档。
同样和我们前面讲的cdk里面其他模块的功能一样,drag-drop使用之前也需要导入DragDropModule模块。
一 CdkDrag
CdkDrag是一个指令。添加了CdkDrag指令的视图,表明该视图是可以拖拽的。
Selector: [cdkDrag]
Exported as: cdkDrag
1.1 CdkDrag属性
属性 | 类型 | 描述 |
---|---|---|
data: T | @Input('cdkDragData') | CdkDrag指令对应组件元素实例上的数据 |
disabled: boolean | @Input('cdkDragDisabled') | 是否禁用从此容器启动拖动序列(我试了下这个属性好像没效果) |
lockAxis: 'x' | 'y' | @Input('cdkDragLockAxis') | 拖曳方向 |
rootElementSelector: string | @Input('cdkDragRootElement') | 传递可拖动元素替代的根元素(换句话说现在是拖动根元素了) |
dropped: EventEmitter<CdkDragDrop<any>> | @Output('cdkDragDropped') | 当一个拖拽的元素松开的时候,并且是在一个container(容器)里面的时候会回调 |
ended: EventEmitter<CdkDragEnd> | @Output('cdkDragEnded') | 拖拽结束的时候调用 |
entered: EventEmitter<CdkDragEnter<any>> | @Output('cdkDragEntered') | 拖拽的时候进入到一个新的容器的时候回调(两个list之间拖拽的时候用到CdkDropList) |
exited: EventEmitter<CdkDragExit<any>> | @Output('cdkDragExited') | 拖拽的时候退出当前容器的时候回调 |
moved: Observable<CdkDragMove<T>> | @Output('cdkDragMoved') | 在拖拽的过程中会回调 |
started: EventEmitter<CdkDragStart> | @Output('cdkDragStarted') | 在开始拖拽的时候调用 |
dropContainer: CdkDropListContainer | 无 | |
element: ElementRef<HTMLElement> | 无 |
上面表格中大量的提到了容器,我们可以把下文中提到的CdkDropList指令对应的视图元素当做是一个视图。
1.2 CdkDrag使用
关于CdkDrag的使用,我们就直接贴代码了,很简单就是在我们想要拖动的视图元素上添加CdkDrag指令就行。
import {Component} from '@angular/core';
@Component({
selector: 'app-drag-drop-drop',
template: `
<div class="dragParent" style="width: 500px; height: 500px; background-color: #f1f1f1">
<div class="example-box" cdkDrag cdkDragRootElement=".dragParent">
可以到处拖拽
</div>
</div>
`,
styles: [`
.example-box {
width: 100px;
height: 100px;
border: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
cursor: move;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: #fff;
border-radius: 4px;
position: relative;
z-index: 1;
transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.example-box:active {
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
`]
})
export class DragDropDropComponent {
}
CdkDrag.gif
上图中因为我设置CdkDrag指令的cdkDragRootElement属性为灰色的div,所以拖动的是整个灰色的div。
二 CdkDropList
Container that wraps a set of draggable items(可以拖拽item的一个集合,说白了就是把一些可拖元素始放到一起来。用CdkDropList指令来表示)。熟悉android的人可以把他看着是一个ListView。
Selector: [cdkDropList] cdk-drop-list
Exported as: cdkDropList
2.1 CdkDropList属性
属性名字 | 类型 | 描述 | |||
---|---|---|---|---|---|
connectedTo: (CdkDropList | string)[] | CdkDropList | string | @Input('cdkDropListConnectedTo') | 想关联的另一个拖拽容器(另一个CdkDropList)。比如你想让item在两个容器间穿梭 |
data: T | @Input('cdkDropListData') | 拖拽容器上的数据(通常是一个数组或者list) | |||
disabled: boolean | @Input('cdkDropListDisabled') | 是否禁用从此容器启动拖动序列 | |||
enterPredicate: (drag: CdkDrag, drop: CdkDropList) => boolean | @Input('cdkDropListEnterPredicate') | 指定哪些item是可以拖拽到当前容器 | |||
id: string | @Input() | 唯一的id,cdkDropListConnectedTo属性的时候可以使用 | |||
lockAxis: 'x' | 'y' | @Input('cdkDropListLockAxis') | 指定容器内所有的item的拖拽放下(垂直,水平) | |||
orientation: 'horizontal' | 'vertical' | @Input('cdkDropListOrientation') | 容器里面item的摆放方向(垂直,水平) | |||
dropped: EventEmitter<CdkDragDrop<T, any>> | @Output('cdkDropListDropped') | 拖拽结束的时候,当有item落到当前容器的时候回调 | |||
entered: EventEmitter<CdkDragEnter<T>> | @Output('cdkDropListEntered') | 当item进入当前容器的时候回调 | |||
exited: EventEmitter<CdkDragExit<T>> | @Output('cdkDropListExited') | 当item离开当前容器的时候回调 | |||
sorted: EventEmitter<CdkDragSortEvent<T>> | @Output('cdkDropListSorted') | 拖拽过程中交换item的时候回调 |
2.2 CdkDropList使用
2.2.1 水平方向的CdkDropList
cdkDropListOrientation属性的使用
import {Component} from '@angular/core';
import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-drag-drop-orientation-drag',
template: `
<div cdkDropList cdkDropListOrientation="horizontal" class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let timePeriod of timePeriods" cdkDrag>{{timePeriod}}</div>
</div>
`,
styles: [`
.example-list {
width: 1000px;
max-width: 100%;
border: solid 1px #ccc;
min-height: 60px;
display: flex;
flex-direction: row;
background: white;
border-radius: 4px;
overflow: hidden;
}
.example-box {
padding: 20px 10px;
border-right: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
flex-grow: 1;
flex-basis: 0;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
`]
})
export class DragDropOrientationDragComponent {
timePeriods = [
'Bronze age',
'Iron age',
'Middle ages',
'Early modern period',
'Long nineteenth century'
];
/**
* 移动item的时候,item之间的位置变换
* @param event
*/
drop(event: CdkDragDrop<string[]>) {
moveItemInArray(this.timePeriods, event.previousIndex, event.currentIndex);
}
}
CdkDropList水平方向.gif
2.2.2 两个list之间数据交换
CdkDropList指令cdkDropListConnectedTo属性的使用。同时为方便大家的使用cdk drag-drop也给提供了transferArrayItem()、moveItemInArray()用于两个list之间交互item和单个list之间item位置变换。
import { Component, OnInit } from '@angular/core';
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-drag-drop-transferring-item',
template: `
<div class="example-container">
<h2>左边list</h2>
<div
cdkDropList
#todoList="cdkDropList"
[cdkDropListData]="leftSource"
[cdkDropListConnectedTo]="[doneList]"
class="example-list"
(cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let item of leftSource" cdkDrag>{{item}}</div>
</div>
</div>
<div class="example-container">
<h2>右边list</h2>
<div
cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="rightSource"
[cdkDropListConnectedTo]="[todoList]"
class="example-list"
(cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let item of rightSource" cdkDrag>{{item}}</div>
</div>
</div>
`,
styles: [`
.example-container {
width: 400px;
max-width: 100%;
margin: 0 25px 25px 0;
display: inline-block;
vertical-align: top;
}
.example-list {
border: solid 1px #ccc;
min-height: 60px;
background: white;
border-radius: 4px;
overflow: hidden;
display: block;
}
.example-box {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
`]
})
export class DragDropTransferringItemComponent {
/**
* 左边类别数据源
*/
leftSource = [
'Get to work',
'Pick up groceries',
'Go home',
'Fall asleep'
];
/**
* 右边列表数据源
*/
rightSource = [
'Get up',
'Brush teeth',
'Take a shower',
'Check e-mail',
'Walk dog'
];
/**
* 拖动的时候,list交换item或者单个list里面item位置的变换
* @param event
*/
drop(event: CdkDragDrop<string[]>) {
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);
}
}
}
CdkDrop交换.gif
2.2.3 可以自己去控制哪些item可以拖进来
我们有一个这样的例子,左边的list里面不接受其他list的数据拖进来,右边的list只接受其他list里面的偶数。代码如下
import {Component} from '@angular/core';
import {CdkDrag, CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-drag-drop-controlling-with-item',
template: `
<div class="example-container">
<h2>不接受其他list的数据</h2>
<div
id="all"
cdkDropList
[cdkDropListData]="all"
cdkDropListConnectedTo="even"
class="example-list"
(cdkDropListDropped)="drop($event)"
[cdkDropListEnterPredicate]="noReturnPredicate">
<div
class="example-box"
*ngFor="let number of all"
[cdkDragData]="number"
cdkDrag>
{{number}}
</div>
</div>
</div>
<div class="example-container">
<h2>只接受其他list里面的偶数</h2>
<div
id="even"
cdkDropList
[cdkDropListData]="even"
cdkDropListConnectedTo="all"
class="example-list"
(cdkDropListDropped)="drop($event)"
[cdkDropListEnterPredicate]="evenPredicate">
<div
class="example-box"
*ngFor="let number of even"
cdkDrag
[cdkDragData]="number">{{number}}
</div>
</div>
</div>
`,
styles: [`
.example-container {
width: 400px;
max-width: 100%;
margin: 0 25px 25px 0;
display: inline-block;
vertical-align: top;
}
.example-list {
border: solid 1px #ccc;
min-height: 60px;
background: white;
border-radius: 4px;
overflow: hidden;
display: block;
}
.example-box {
padding: 20px 10px;
border-bottom: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
`]
})
export class DragDropControllingWithItemComponent {
all = [1, 2, 3, 4, 5, 6, 7, 8, 9];
even = [10];
drop(event: CdkDragDrop<string[]>) {
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);
}
}
/** 只接受其他list里面的偶数 */
evenPredicate(item: CdkDrag<number>) {
return item.data % 2 === 0;
}
/** 不让其他list里面的数据移入到这个里面来 */
noReturnPredicate() {
return false;
}
}
可控制的item.gif
三 CdkDropListGroup
CdkDropListGroup指令的范围比CdkDropList要大一级。CdkDropListGroup可以包含多个CdkDropList。而且当CdkDropListGroup包含多个CdkDropList的时候,这些CdkDropList直接是相互connect的(CdkDropList就不用去写cdkDropListConnectedTo属性了)。
Selector: [cdkDropListGroup]
Exported as: cdkDropListGroup
CdkDropListGroup使用场景:当我们在多个list之间的item需要相互拖动的时候派上用场。
四 CdkDragHandle
Handle that can be used to drag and CdkDrag instance。拖动CdkDrag实例的句柄,简单来说就是指定一个元素。当我们拖动这个元素的时候整个CdkDrag实例都会动。
4.1 CdkDragHandle的使用
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-drag-drop-handle-drag-area',
template: `
<div class="example-box" cdkDrag>
I can only be dragged using the handle
<div class="example-handle" cdkDragHandle>
<svg width="24px" fill="currentColor" viewBox="0 0 24 24">
<path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z">
</path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</div>
</div>
`,
styles: [`
.example-box {
width: 200px;
height: 200px;
padding: 10px;
box-sizing: border-box;
border: solid 1px #ccc;
color: rgba(0, 0, 0, 0.87);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: #fff;
border-radius: 4px;
position: relative;
z-index: 1;
transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1);
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.example-box:active {
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.example-handle {
position: absolute;
top: 10px;
right: 10px;
color: #ccc;
cursor: move;
width: 24px;
height: 24px;
}
`]
})
export class DragDropHandleDragAreaComponent {
}
CdkDragHandle.gif
五 CdkDragPreview
Element that will be used as a template for the preview of a CdkDrag when it is being dragged。简单点就是drag拖动过程中拖动的item的展示形式。
5.1 CdkDragPreview的使用
比如一个这里的例子,展示影片名字列表,在拖动的时候展示影片对应的图片。
import {Component, OnInit} from '@angular/core';
import {CdkDragDrop, moveItemInArray} from '@angular/cdk/drag-drop';
@Component({
selector: 'app-drag-drop-customizing-drag-preview',
template: `
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let movie of movies" cdkDrag>
{{movie.title}}
<img *cdkDragPreview [src]="movie.poster" [alt]="movie.title">
</div>
</div>
`,
styles: [`
.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;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-placeholder {
opacity: 0;
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
`]
})
export class DragDropCustomizingDragPreviewComponent {
movies = [
{
title: 'Episode I - The Phantom Menace',
poster: 'https://upload.wikimedia.org/wikipedia/en/4/40/Star_Wars_Phantom_Menace_poster.jpg'
},
{
title: 'Episode II - Attack of the Clones',
poster: 'https://upload.wikimedia.org/wikipedia/en/3/32/Star_Wars_-_Episode_II_Attack_of_the_Clones_%28movie_poster%29.jpg'
},
{
title: 'Episode III - Revenge of the Sith',
poster: 'https://upload.wikimedia.org/wikipedia/en/9/93/Star_Wars_Episode_III_Revenge_of_the_Sith_poster.jpg'
},
{
title: 'Episode IV - A New Hope',
poster: 'https://upload.wikimedia.org/wikipedia/en/8/87/StarWarsMoviePoster1977.jpg'
},
{
title: 'Episode V - The Empire Strikes Back',
poster: 'https://upload.wikimedia.org/wikipedia/en/3/3c/SW_-_Empire_Strikes_Back.jpg'
},
{
title: 'Episode VI - Return of the Jedi',
poster: 'https://upload.wikimedia.org/wikipedia/en/b/b2/ReturnOfTheJediPoster1983.jpg'
},
{
title: 'Episode VII - The Force Awakens',
poster: 'https://upload.wikimedia.org/wikipedia/en/a/a2/Star_Wars_The_Force_Awakens_Theatrical_Poster.jpg'
},
{
title: 'Episode VIII - The Last Jedi',
poster: 'https://upload.wikimedia.org/wikipedia/en/7/7f/Star_Wars_The_Last_Jedi.jpg'
}
];
drop(event: CdkDragDrop<{ title: string, poster: string }[]>) {
moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
}
}
CdkDragPreview.gif
六 CdkDragPlaceholder
Element that will be used as a template for the placeholder of a CdkDrag when it is being dragged. The placeholder is displayed in place of the element being dragged。简单来说就是drag过程中,drag item对应的container里面item的占位符。
6.1 CdkDragPlaceholder使用
如下的例子,拖动的时候,CdkDragPlaceholder对应加个灰色的背景
import {Component} from '@angular/core';
import {CdkDragDrop, moveItemInArray} from "@angular/cdk/drag-drop";
@Component({
selector: 'app-drag-drop-customizing-drag-place-holder',
template: `
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let movie of movies" cdkDrag>
<div class="example-custom-placeholder" *cdkDragPlaceholder></div>
{{movie}}
</div>
</div>
`,
styles: [`
.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;
flex-direction: row;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: move;
background: white;
font-size: 14px;
}
.cdk-drag-preview {
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
.cdk-drag-animating {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-box:last-child {
border: none;
}
.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
.example-custom-placeholder {
background: #ccc;
border: dotted 3px #999;
min-height: 60px;
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
`]
})
export class DragDropCustomizingDragPlaceHolderComponent {
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',
'Episode VI - Return of the Jedi',
'Episode VII - The Force Awakens',
'Episode VIII - The Last Jedi'
];
drop(event: CdkDragDrop<string[]>) {
moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
}
}
CdkDragPlaceholder.gif
关于cdk里面drag-drop的内容咱们就先将这么多,主要就是各个指令的使用。文章中涉及的代码例子 https://github.com/tuacy/angular-cdk-study
网友评论