Directive not responding due to same mouse events

2020-02-16 03:37发布

I have a feature where box can be dragged and dropped into the grey area(Please refer to the stackblitz link) Once the box is dragged and dropped , the box can only be moved within the grey area by clicking on the pink color of the box.

Resizing functionality has also been added, so the box can be resized.

Before resize directive was added the box can only be moved within grey area but after adding adding the resize directive when we resize, the box starts moving out of the grey area , the issue is the box should not move out of the grey area when resizing is done

Stackblitz link

https://stackblitz.com/edit/angular-rgeq2p?file=src/app/hello.component.html

hello.component.html [ has the box and directives applied on the box ]

<div appMovableArea appDropzone (drop)="move(currentBox, dropzone1)">
<div *ngFor="let box of dropzone1"
      appDroppable
     (dragStart)="currentBox = box"
      appMovable
      resize>
    {{ box.dis }}
</div>
</div>

where the box has (dragStart) output event emitter which is bound to draggable directive([appDroppable + draggable] and for the move functionality appMovable, appMovableArea directives are there) .And the events are shared among directives using Droppable.service.ts

The (drop) is an output event emitter applied on grey area using dropzone[appDropzone] directive

import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, 
    OnInit, Output, SkipSelf } from '@angular/core';
import { DroppableService } from './droppable.service';

@Directive({
     selector: '[appDropzone]',
     providers: [DroppableService]
    })
export class DropzoneDirective implements OnInit {

     @Output() drop = new EventEmitter<PointerEvent>();
     @Output() remove = new EventEmitter<PointerEvent>();
     private clientRect: ClientRect;

constructor(@SkipSelf() private allDroppableService: DroppableService,
              private innerDroppableService: DroppableService,
              private element: ElementRef) { }

ngOnInit(): void {
        this.allDroppableService.dragStart$.subscribe(() => 
        this.onDragStart());
        this.allDroppableService.dragEnd$.subscribe(event => 
        this.onDragEnd(event));

        this.allDroppableService.dragMove$.subscribe(event => {
          if (this.isEventInside(event)) {
        this.onPointerEnter();
      } else {
        this.onPointerLeave();
      }
     });
        this.innerDroppableService.dragStart$.subscribe(() => 
        this.onInnerDragStart());
        this.innerDroppableService.dragEnd$.subscribe(event => 
        this.onInnerDragEnd(event));
}

private onPointerEnter(): void {
     if (!this.activated) {
      return;
     }
     this.entered = true;
     }

private onPointerLeave(): void {
        if (!this.activated) {
         return;
      }
     this.entered = false;
     }

private onDragStart(): void {
       this.clientRect = this.element.nativeElement.getBoundingClientRect();
       this.activated = true;
      }

private onDragEnd(event: PointerEvent): void {
        if (!this.activated) {
         return;
      }
      if (this.entered) {
        this.drop.emit(event);
      }
     }

private onInnerDragStart() {
    this.activated = true;
    this.entered = true;
    }

private onInnerDragEnd(event: PointerEvent) {
    if (!this.entered) {
      this.remove.emit(event);
    }
  }

private isEventInside(event: PointerEvent) {
    return event.clientX >= this.clientRect.left &&
      event.clientX <= this.clientRect.right &&
      event.clientY >= this.clientRect.top &&
      event.clientY <= this.clientRect.bottom;
  }
}

Then on the box (dragStart) output event emitter which is present indraggable directive[appDraggable] which listens for pointerdown events

import { Directive, EventEmitter, HostBinding, HostListener, Output, 
ElementRef } from '@angular/core';
@Directive({
 selector: '[appDraggable],[appDroppable]'
})
export class DraggableDirective {
 @Output() dragStart = new EventEmitter<PointerEvent>();
 @Output() dragMove = new EventEmitter<PointerEvent>();
 @Output() dragEnd = new EventEmitter<PointerEvent>();

 constructor(public element: ElementRef) {}

 @HostListener('pointerdown', ['$event'])
   onPointerDown(event: PointerEvent): void {
    if (event.button !== 0) {
    return;
   }
   this.pointerId = event.pointerId;
   this.dragging = true;
   this.dragStart.emit(event); 
   }

 @HostListener('document:pointermove', ['$event'])
    onPointerMove(event: PointerEvent): void {
     if (!this.dragging || event.pointerId !== this.pointerId) {
     return;
    } 
    this.dragMove.emit(event);
    }

 @HostListener('document:pointercancel', ['$event'])
 @HostListener('document:pointerup', ['$event'])
     onPointerUp(event: PointerEvent): void {
    if (!this.dragging || event.pointerId !== this.pointerId) {
      return;
    }
    this.dragging = false;
    this.dragEnd.emit(event);
    }
   }

Movable directive for maintaining move inside the grey area which in turn uses calculation based from movable-area directive

import { Directive, ElementRef, HostBinding, HostListener, Input } from 
'@angular/core';
import { DraggableDirective } from './draggable.directive';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';

interface Position {
 x: number;
 y: number;
}

@Directive({
 selector: '[appMovable]'
})
export class MovableDirective extends DraggableDirective {
  @HostBinding('style.transform') get transform(): SafeStyle {
   return this.sanitizer.bypassSecurityTrustStyle(
  `translateX(${this.position.x}px) translateY(${this.position.y}px)`
  );
  }

  @HostBinding('class.movable') movable = true;
  position: Position = {x: 0, y: 0};
  private startPosition: Position;
  @Input('appMovableReset') reset = false;

constructor(private sanitizer: DomSanitizer, public element: ElementRef) {
   super(element);
 }

@HostListener('dragStart', ['$event'])
   onDragStart(event: PointerEvent) {
     this.startPosition = {
     x: event.clientX - this.position.x,
     y: event.clientY - this.position.y
 }
 }

@HostListener('dragMove', ['$event'])
   onDragMove(event: PointerEvent) {
    this.position.x = event.clientX - this.startPosition.x;
    this.position.y = event.clientY - this.startPosition.y;
 }

@HostListener('dragEnd', ['$event'])
   onDragEnd(event: PointerEvent) {
    if (this.reset) {
     this.position = {x: 0, y: 0};
 }
 }
}

The resize directive is also present in the stackblitz link. And styles for resize directive are present in styles.css

2条回答
ら.Afraid
2楼-- · 2020-02-16 04:04

One small hack you can do is add 'resizing' class to the element in resize directive and check whether that class is present in draggable directive if present do not make it draggable. I can't get your code because it's so difficult. I don't know why you made this so complex.

查看更多
我命由我不由天
3楼-- · 2020-02-16 04:15

Here is implementation of Drag&Drop directive, sorry can't fix your code because it's a mess same as resize:

Directive

@Directive({
  selector: '[draggable]'
})
class Draggable implements onInit {
    private element: HTMLElement;
    private handlerNode: HTMLElement;
    private data: {x: number, y: number};

    @Input('draggable')
    private handler: string;

    @HostListener('mousedown', ['$event'])
    mousedown(e) {
        if (e.target === this.handlerNode) {
            var rect = this.element.getBoundingClientRect();
            this.data = {
                x: e.clientX - rect.left,
                y: e.clientY - rect.top
            };
        } else {
            delete this.data;
        }
    }

    @HostListener('document:mouseup', ['$event'])
    mouseup(e) {
        delete this.data;
    }
    constructor(@Inject(ElementRef) element: ElementRef) {
        this.element = element.nativeElement;
    }
    ngOnInit() {
        this.element.classList.add('dragabble');
        this.handlerNode = this.element.querySelector(this.handler);
    }

    @HostListener('document:mousemove', ['$event'])
    onPointerMove(e: PointerEvent): void {
        if (this.data) {
            var x = e.clientX - this.data.x;
            var y = e.clientY - this.data.y;
            this.element.style.left = x + 'px';
            this.element.style.top = y + 'px';
        }
    }
}

CSS

.dragabble .handler {
    position: absolute;
    width: calc(100% - 12px);
    height: calc(100% - 12px);
    left: 6px;
    top: 6px;
}

Template:

<div [resize]="toggle" style="left: 100px; top: 50px"
     [draggable]="'.handler'">
  <div class="handler">xxx</div>
</div>

Demo: https://codepen.io/jcubic/pen/wvwJNqQ?editors=0110

查看更多
登录 后发表回答