angular8指令式拖动实现

219 阅读1分钟

效果展示

动画.gif

主要代码

拖动代码
import {
  Directive,
  ElementRef,
  HostListener,
  Input,
  Renderer2,
} from "@angular/core";
import { DragDropService } from "./drag-drop.service";

@Directive({
  selector: "[appDrag]",
})
export class DragDirective {
  private _isDraggable = false;
  @Input() dragTag: string;
  @Input() dragData: any;
  @Input() isClone = true;
  @Input() dragClass = "dragging";

  constructor(
    private el: ElementRef,
    private rd: Renderer2,
    private service: DragDropService
  ) {}
  /**
   * @description 获取目标元素并给他设置是否可拖拽
   * @memberof DragDirective
   */
  @Input("appDrag")
  set isDraggable(val) {
    this._isDraggable = val;
    this.rd.setAttribute(this.el.nativeElement, "draggable", `${val}`);
  }

  get isDraggable() {
    return this._isDraggable;
  }

  @HostListener("dragstart", ["$event"])
  ondragStart(ev: Event) {
    if (this.el.nativeElement === ev.target) {
      this.service.setDragData({
        tag: this.dragTag,
        data: Object.assign({}, this.dragData),
        el: this.el,
      });
      this.rd.addClass(this.el.nativeElement, this.dragClass);
    }
  }

  @HostListener("dragend", ["$event"])
  onDragEnd(ev: Event) {
    if (this.el.nativeElement === ev.target) {
      this.service.setDragData({
        tag: this.dragTag,
        data: Object.assign({}, this.dragData),
        el: this.el,
      });
      this.rd.removeClass(this.el.nativeElement, this.dragClass);
    }
  }
}

放代码
import {
  Directive,
  ElementRef,
  EventEmitter,
  HostListener,
  Input,
  Output,
  Renderer2,
} from "@angular/core";
import { take } from "rxjs/operators";
import { DragData, DragDropService } from "./drag-drop.service";

@Directive({
  selector: "[appDrop]",
})
export class DropDirective {
  @Input() dropTags: string[] = []; // 目标区域
  @Output() dropend = new EventEmitter<DragData>();

  private _datas;
  private x;
  private y;
  constructor(
    private el: ElementRef,
    private rd: Renderer2,
    private service: DragDropService
  ) {
    this._datas = this.service.getDragData().pipe(take(1)); // take 获取n个值
  }

  @HostListener("dragenter", ["$event"]) // drag的对象进入目标区域
  onDragEnter(ev: Event) {
    ev.preventDefault();
    ev.stopPropagation();

    if (ev.target === this.el.nativeElement) {
      console.log(ev, 1);
    }
  }
  /**
   * //需要支持多级拖拽,所以要防止事件冒泡
   * //dragover允许进行data transfer的一些特效
   * @param ev
   */
  @HostListener("dragover", ["$event"]) // drag的对象在目标区域上面
  onDragOver(ev: Event) {
    ev.preventDefault();
    ev.stopPropagation();
    console.log("000");
    this._datas.subscribe((datas) => {
      if (datas && this.dropTags.indexOf(datas.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"]) // drag的对象离开目标区域上面
  onDragLeave(ev: Event) {
    ev.preventDefault();
    ev.stopPropagation();
  }
  /**
   * 注意:在拖拽事件中遇到目标元素ondrop事件没有效果的原因:需要在目标元素上写ondragover事件,阻止ondragover的默认行为,然后ondrop事件才起作用
   * @param ev
   */
  @HostListener("drop", ["$event"]) // 监听放的事件
  onDrop(ev: Event) {
    ev.preventDefault();
    ev.stopPropagation();
    if (ev.target === this.el.nativeElement) {
      this._datas.subscribe((datas) => {
        if (datas && this.dropTags.indexOf(datas.tag) > -1) {
          const rectInfo = this.el.nativeElement.getBoundingClientRect();
          datas.x =
            ev["pageX"] -
            rectInfo.left -
            datas.el.nativeElement.offsetWidth / 2;
          datas.y =
            ev["pageY"] -
            rectInfo.top -
            datas.el.nativeElement.offsetHeight / 2;
          datas.width = datas.el.nativeElement.offsetWidth;
          datas.height = datas.el.nativeElement.offsetHeight;
          datas.rotate = 0;
          this.dropend.emit(datas); // drop的时候把dragData发射出去
          this.service.clearDragData(); // drop的时候把data clear掉,否则会影响下一次拖拽

          //   console.log(ev, 3);
          //   datas.el.style.position = "absolute";
          //   datas.el.style.left = ev.pageX - 210 + "px";
          //   datas.el.style.top = ev.pageY - 70 + "px";
          //   datas.el.setAttribute("isClone", "false");
          //   datas.el.setAttribute("dragTag", "main");
          //   this.rd.appendChild(this.el.nativeElement, datas.el);
          //   this.service.setDragData(null);
        }
      });
    }
  }
}

使用

<com-container class="contain" [title]="item.key" [config]="item.value" [appDrag]="true" [dragTag]="'left'"
      [dragData]="item.value.data">
    </com-container>
 <div class="container" appDrop [dropTags]="tags" (dropend)="dropend($event)">
    <div *ngFor="let item of listData;let i=index " class="cont">
      <com-container class="contain" [attr.index]="i" [ngStyle]="{'left': item.x+'px','top':item.y+'px'}" [title]=""
        [config]="{'index':i,'data':item}" [appDrag]="true" [dragTag]="'main'" [dragData]="item.data"
        [isActive]="i===selectedIndex" (click)="setProp(i,item,$event)">
      </com-container>
    </div>

完整代码