Skip to content

Instantly share code, notes, and snippets.

@HydrangeaPurple
Last active August 10, 2021 18:23
Show Gist options
  • Save HydrangeaPurple/b8da3181042553041b3419620e078ec7 to your computer and use it in GitHub Desktop.
Save HydrangeaPurple/b8da3181042553041b3419620e078ec7 to your computer and use it in GitHub Desktop.
angular 拖拽指令示例
/* 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!);
}
}
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';
}
}
}
/* 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