前言:最近项目要实现一个拖拽功能,我在网上开始了各类搜寻,虽然后面因为数据原因舍弃了拖拽的这一需求,但是为了不辜负最近的研究,还是来记录一下。
场景需求:面试预约选时间节点,候选人之间是可以相互交换的,但是局限于面试方向相同的候选人才能相互拖拽(拖拽后即表示两个候选人之间交换面试时间)。本来此种场景上图更为明确,奈何公司只限于内网开发,上传不了图片,嘤嘤嘤。。。
正文:
draggable是H5新增的属性,true表示可以拖拽,false表示不能拖拽。在拖拽过程中分别有两个元素:被拖动元素与目标元素。
被拖动元素相关的事件:
ondragstart:按下鼠标并开始移动时触发该事件。
ondrag:在start事件触发后被触发,并在鼠标移动过程中不停被触发。
ondragend:当拖动停止触发(无论把元素放到拖放目标上还是在无效目标上)
目标元素相关的事件:
ondragenter:被拖动元素进入目标时触发。
ondragover:被拖动元素在目标元素上移动时被触发。
ondragleave:被拖动元素离开目标时触发,即拖到无效目标时。
ondrop:被拖动元素被放置在目标上时被触发。
注意:默认情况下目标元素是不允许被放置的,所以不会触发drop事件。这时需要在ondragover中阻止默认行为才能成为被允许放置的目标。
对于angular、vue等依赖组件开发的框架,最好把draggable封装成指令去实现功能。(实施步骤与参考文章无异,直接上代码啦,其他不赘述)具体步骤:
1.新建directives指令文件夹
2.新建拖拽文件夹directives/drap-drop
3.新建drap指令文件directives/drag-drop/drag.directive.ts
4.新建drop指令文件directives/drag-drop/drop.directive.ts
5.因为涉及数据交换,所以要新建一个services来传递数据,directives/directives.services.ts
6.新建directives/directives.module.ts
7.在公共模板中引入DirectiveModule
8.引用
directives.module.ts
import { NgModule } from '@angular/core';
import { DragDirective } from'./drag-drop/drag.directive';
import { DropDirective } from'./drag-drop/drop.directive';
import { DragDropService } from'./drag-drop/drag-drop.service';
@NgModule({
declarations: [DragDirective, DropDirective],
exports: [DragDirective, DropDirective],
providers:[DragDropService]
})
export class DirectiveModule { }
drag.directive.ts
import { Directive,HostBinding, HostListener, Host, ElementRef, Renderer2, Input,Output,EventEmitter } from '@angular/core';
import {DragDropService} from'./drag-drop.service';
@Directive({
selector:'[app-draggable][dragTag][dragData][draggedClass]'})
export class DragDirective {
private _isDraggble= false;/*给标签设置draggable属性
*true-能拖拽
*false-不能拖拽*/@Input('app-draggable')
set isDraggable(value:boolean){this._isDraggble=value;this.rd.setAttribute(this.el.nativeElement,'draggable',`${value}`);
}
get isDraggable(){return this._isDraggble;
}//拖拽开始时添加的class
@Input()
draggedClass:string;//给拖拽的元素定义dragTag标识(目标元素也会设置一个标识,用于判断该元素是否能拖到目标元素)
@Input()
dragTag:any;//给DragDropservice传递的数据(即目标元素的数据)
@Input()
dragData:any//判断开始/结束拖拽时,部分区域的元素是不能设置为拖拽目标的(给不能拖拽的区域设置背景色)
@Output()
dragStart= new EventEmitter();
@Output()
dragEnd= new EventEmitter();
constructor(private el:ElementRef, private rd:Renderer2,private service:DragDropService) { }
@HostListener('dragstart', ['$event'])
ondragstart(ev:Event){//判断drag元素是不是指令应用的元素发起的
if(this.el.nativeElement===ev.target){this.rd.addClass(this.el.nativeElement, this.draggedClass);//往el上增加一个class
//进入时候给service传递上数据
this.service.setDragData({tag:this.dragTag,data:this.dragData});
ev['dataTransfer'].setData('tag',this.dragTag);//兼容firefox,用来设置拖放操作的drag data到指定的数据和类型
this.dragStart.emit(this.dragTag);
}
}
@HostListener('dragend', ['$event'])
ondragend(ev:Event){if(this.el.nativeElement===ev.target){this.rd.removeClass(this.el.nativeElement, this.draggedClass);this.dragEnd.emit(this.dragTag);
}
}
}
drop.directive.ts
import { Directive, HostListener, ElementRef, Renderer2, Input, Output, EventEmitter } from '@angular/core';
import { DragDropService, DragData } from'./drag-drop.service';
import { take } from'rxjs/operators';
@Directive({
selector:'[dropTag]'})
export class DropDirective {/*拖拽至目标元素时发射的事件*/@Output()
dropped= new EventEmitter();/*设定目标区域的标识*/@Input()
dropTag:any;
private data$;
constructor(private el:ElementRef, private rd:Renderer2,private service:DragDropService) {this.data$ = this.service.getDragData().pipe(take(1));
}
@HostListener('dragenter', ['$event'])
onDragEnter(ev:Event){//判断drag元素是不是指令应用的元素发起的
if(this.el.nativeElement===ev.target){this.data$.subscribe(dragData=>{if(dragData && this.dropTag ==dragData.tag){
}
});
}
}//dragover允许进行data transfer的一些特效
@HostListener('dragover', ['$event'])
onDragOver(ev:Event){//需要支持多级拖拽,所以要防止事件冒泡
ev.preventDefault();
ev.stopPropagation();if(this.el.nativeElement===ev.target){this.data$.subscribe(dragData=>{if(dragData && this.dropTag ==dragData.tag){this.rd.setProperty(ev,'dataTransfer.effectAllowed','all');this.rd.setProperty(ev,'dataTransfer.fropEffect','move');
}else{this.rd.setProperty(ev,'dataTransfer.effectAllowed','none');this.rd.setProperty(ev,'dataTransfer.dropEffect','none');
}
});
}
}
@HostListener('dragleave', ['$event'])
onDragLeave(ev:Event){
ev.preventDefault();
ev.stopPropagation();if(this.el.nativeElement===ev.target){this.data$.subscribe(dragData=>{if(dragData && this.dropTag ==dragData.tag){
}
});
}
}
@HostListener('drop', ['$event'])
onDrop(ev:Event){
ev.preventDefault();
ev.stopPropagation();if(this.el.nativeElement===ev.target){this.data$.subscribe(dragData =>{if(dragData && this.dropTag =