Last active
August 10, 2021 18:23
-
-
Save HydrangeaPurple/b8da3181042553041b3419620e078ec7 to your computer and use it in GitHub Desktop.
angular 拖拽指令示例
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-non-null-assertion */ | |
import { Injectable } from '@angular/core'; | |
import { Observable, BehaviorSubject } from 'rxjs'; | |
import { DragData } from '../../entity/interface'; | |
@Injectable({ | |
providedIn: 'root', | |
}) | |
export class DragDropService { | |
// 用BehaviorSubject总能记住上一次的值 | |
private _dragData = new BehaviorSubject<DragData>(null!); | |
private _lastDragEnterElement = new BehaviorSubject<HTMLElement>(null!); | |
constructor() { } | |
setDragData(data: DragData) { | |
this._dragData.next(data); | |
} | |
getDragData(): Observable<DragData> { | |
return this._dragData.asObservable(); | |
} | |
clearDragData(): void { | |
this._dragData.next(null!); | |
} | |
setHtmlElement(elem: HTMLElement) { | |
this._lastDragEnterElement.next(elem); | |
} | |
getHtmlElement(): Observable<HTMLElement> { | |
return this._lastDragEnterElement.asObservable(); | |
} | |
clearHtmlElement(): void { | |
this._lastDragEnterElement.next(null!); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Directive, HostListener, Host, ElementRef, Renderer2, Input, } from '@angular/core'; | |
import { DragDropService } from '../drag-drop/drag-drop.service'; | |
@Directive({ | |
selector: '[app-draggable][dragTag][dragData][draggedClass][dragFromComponent]', | |
}) | |
export class DragDirective { | |
@Input() | |
draggedClass!: string; | |
// 多定义一个dragTag | |
@Input() | |
dragTag!: string; | |
// 给DragDropservice传递的数据 | |
@Input() | |
dragData!: any; | |
// eslint-disable-next-line @angular-eslint/no-input-rename | |
@Input('app-draggable') | |
set isDraggable(value: boolean) { | |
this._isDraggble = value; | |
this.rd.setAttribute(this.el.nativeElement, 'draggable', `${value}`); | |
if (value) { | |
this.el.nativeElement.style.cursor = 'grab'; | |
} | |
} | |
get isDraggable(): boolean { | |
return this._isDraggble; | |
} | |
/** | |
* 拖拽来源组件(可选参数) | |
* | |
* @type {*} | |
* @memberof DragDirective | |
*/ | |
@Input() | |
dragFromComponent!: any; | |
private _isDraggble = false; | |
private isDown = false; | |
constructor( | |
private el: ElementRef, | |
private rd: Renderer2, | |
// 注入DragDropService | |
private service: DragDropService | |
) { } | |
// 拖拽开始 | |
@HostListener('dragstart', ['$event']) | |
ondragstart(ev: Event) { | |
// 判断drag元素是不是指令应用的元素发起的 | |
if (this.el.nativeElement === ev.target) { | |
// 往el上增加一个class | |
this.rd.addClass(this.el.nativeElement, this.draggedClass); | |
// 进入时候给service传递上数据 | |
this.service.setDragData({ | |
tag: this.dragTag, | |
data: this.dragData, | |
dragFromComponent: this.dragFromComponent, | |
}); | |
} | |
} | |
@HostListener('dragend', ['$event']) | |
ondragend(ev: Event) { | |
if (this.el.nativeElement === ev.target) { | |
this.rd.removeClass(this.el.nativeElement, this.draggedClass); | |
this.el.nativeElement.style.cursor = 'grab'; | |
} | |
} | |
// 点击事件绑定样式 | |
@HostListener('mousedown', ['$event']) onMousedown(event: MouseEvent) { | |
if (this._isDraggble) { | |
this.isDown = true; | |
this.el.nativeElement.style.cursor = 'grabbing'; | |
} | |
} | |
// 点击事件还原样式样式 | |
@HostListener('document:mouseup', ['$event']) onMouseup(event: MouseEvent) { | |
// 只用当元素移动过了,离开函数体才会触发。 | |
if (this.isDraggable && this.isDown) { | |
this.isDown = false; | |
this.el.nativeElement.style.cursor = 'grab'; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-non-null-assertion */ | |
import { Directive, ElementRef, EventEmitter, HostListener, Input, Output, Renderer2 } from '@angular/core'; | |
import { Observable } from 'rxjs'; | |
import { take } from 'rxjs/operators'; | |
import { DragData } from '../../entity/interface'; | |
import { DragDropService } from '../drag-drop/drag-drop.service'; | |
/** | |
* 拖拽组件 | |
* | |
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/drag_event | |
* @export | |
* @class DropDirective | |
*/ | |
@Directive({ | |
selector: '[app-droppable][dropTags][dragEnterClass]', | |
}) | |
export class DropDirective { | |
@Output() | |
dropped = new EventEmitter<DragData>(); | |
@Input() | |
dragEnterClass!: string; | |
@Input() | |
dropTags: string[] = []; | |
private data$: Observable<DragData>; | |
private lastElem$: Observable<HTMLElement>; | |
constructor( | |
private el: ElementRef, | |
private rd: Renderer2, | |
private service: DragDropService | |
) { | |
this.data$ = this.service.getDragData().pipe(take(1)); | |
this.lastElem$ = this.service.getHtmlElement().pipe(take(1)); | |
} | |
@HostListener('dragenter', ['$event']) | |
onDragEnter(ev: Event) { | |
this.service.setHtmlElement(this.el.nativeElement); | |
// 判断drag元素是不是指令应用的元素发起的 | |
if (this.el.nativeElement === ev.target) { | |
this.data$.subscribe((dragData) => { | |
if (this.dropTags.indexOf(dragData.tag) > -1) { | |
// 往el上增加一个class | |
this.rd.addClass( | |
this.el.nativeElement, | |
this.dragEnterClass | |
); | |
// 特殊情况 onDragForceStop用 | |
(this.el.nativeElement as any).dragEnterClass = this.dragEnterClass; | |
} | |
}); | |
} | |
} | |
// dragover允许进行data transfer的一些特效 | |
@HostListener('dragover', ['$event']) | |
onDragOver(ev: Event) { | |
// 需要支持多级拖拽,所以要防止事件冒泡 | |
ev.preventDefault(); | |
ev.stopPropagation(); | |
this.lastElem$.subscribe(elem => { | |
// 判断drag元素是不是指令应用的元素发起的 | |
if (this.el.nativeElement === ev.target || this.el.nativeElement === elem) { | |
this.data$.subscribe(dragData => { | |
if (this.dropTags.indexOf(dragData.tag) > -1) { | |
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(); | |
// 判断drag元素是不是指令应用的元素发起的 | |
if (this.el.nativeElement === ev.target) { | |
this.lastElem$.subscribe(elem => { | |
if (this.el.nativeElement !== elem) { | |
this.data$.subscribe(dragData => { | |
if (this.dropTags.indexOf(dragData.tag) > -1) { | |
this.rd.removeClass(this.el.nativeElement, this.dragEnterClass); // 往el上增加一个class | |
} | |
}); | |
} | |
}); | |
} | |
} | |
@HostListener('dragend', ['$event']) | |
onDragForceStop(ev: Event) { | |
ev.preventDefault(); | |
ev.stopPropagation(); | |
this.lastElem$.subscribe(elem => { | |
this.rd.removeClass(elem, this.dragEnterClass); // 往el上增加一个class | |
// 特殊情况 | |
this.rd.removeClass(elem, (elem as any).dragEnterClass); // 往el上增加一个class | |
this.service.clearDragData(); // drop的时候把data clear掉,否则会影响下一次拖拽 | |
this.service.clearHtmlElement(); | |
}); | |
} | |
@HostListener('drop', ['$event']) | |
onDrop(ev: Event) { | |
ev.preventDefault(); | |
ev.stopPropagation(); | |
this.lastElem$.subscribe(elem => { | |
// 判断drag元素是不是指令应用的元素发起的 | |
if (this.el.nativeElement === ev.target || this.el.nativeElement === elem) { | |
this.data$.subscribe(dragData => { | |
if (this.dropTags.indexOf(dragData.tag) > -1) { | |
this.rd.removeClass(this.el.nativeElement, this.dragEnterClass); // 往el上增加一个class | |
this.dropped.emit(dragData); // drop的时候把dragData发射出去 | |
this.service.clearDragData(); // drop的时候把data clear掉,否则会影响下一次拖拽 | |
this.service.clearHtmlElement(); | |
} | |
}); | |
} else { | |
this.service.clearHtmlElement(); | |
} | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment