【问题标题】:Directive not responding due to same mouse events being used in multiple directives on same element由于在同一元素上的多个指令中使用了相同的鼠标事件,指令没有响应
【发布时间】:2019-12-31 12:37:11
【问题描述】:

我有一个可以将盒子拖放到灰色区域的功能(请参阅stackblitz链接) 拖放框后,只能通过单击框的粉红色在灰色区域内移动框。

还添加了调整大小的功能,因此可以调整框的大小。

在添加resize指令之前,盒子只能在灰色区域内移动,但是添加resize指令后,当我们调整大小时,盒子开始移出灰色区域,问题是盒子不应该移出灰色区域调整大小时的区域

Stackblitz 链接

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

hello.component.html [盒子和指令应用在盒子上]

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

盒子有 (dragStart) 输出事件发射器,它绑定到可拖动指令 ([appDroppable + draggable] 和 移动功能 appMovable、a​​ppMovableArea 指令在那里)。并且使用 Droppable.service.ts 在指令之间共享事件

(drop) 是一个输出事件发射器,使用 dropzone[appDropzone] 指令

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;
  }
}

然后在 (dragStart) 框上输出事件发射器,该发射器存在于 draggable 指令[appDraggable] 中,它侦听指针向下事件

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 指令用于保持在灰色区域内移动 turn 使用基于可移动区域指令的计算

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};
 }
 }
}

resize 指令也出现在 stackblitz 链接中。 调整大小指令的样式存在于styles.css中

【问题讨论】:

  • @jcubic ,请帮忙
  • 你不能阻止一个鼠标在同一个元素上移动另一个鼠标,尝试在父元素上拖动并在子元素上调整大小。
  • @Enthu 有什么问题?我尝试调整大小,但它仍然保持边界
  • @NidhinJoseph ,当我们调整大小时,盒子开始移动并走出灰色区域,谢谢

标签: html css angular angular-directive


【解决方案1】:

这是 Drag&Drop 指令的实现,抱歉无法修复您的代码,因为它与调整大小相同:

指令

@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;
}

模板

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

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

【讨论】:

  • 感谢您的回答,但我的代码也具有 drop 和 drop area 依赖关系,所有指令都是相互关联的,如果我想通过您的实现,那么我将不得不实现 drop 和 gray区域边界(指令),如果你能建议你的调整大小指令如何适合我现有的代码,那将非常有帮助
  • @Enthu 只看代码,你不应该要求为你写完整的代码。在这里,您有解决方案,拖放处理程序。在框内使用选择器而不是整个框。如果您想成为程序员,您需要知道如何将解决方案合并到您自己的代码中。如果你不这样做,那么你将无法找到任何解决方案,任何问题,因为它会与你的有点不同。可能有很多教程和文章展示了如何使用你需要的一切。您只需将它们全部写在一个代码中。
  • 实施不当。如果您有一个包含 100 个元素的可拖动列表,则每个元素中的 document:mouseup 都会使事件队列饱和,并且导航器开始出现滞后。
  • @Antoniodelamata 如果您知道更好的实现可以使用这么多元素,您可以编写自己的答案。
【解决方案2】:

您可以做的一个小技巧是将'resizing' 类添加到resize 指令中的元素,并检查该类是否存在于可拖动指令中,如果存在则不要使其可拖动。我无法获取您的代码,因为它太难了。我不知道你为什么把这个弄得这么复杂。

【讨论】:

  • 因为我必须处理拖放操作并保持放置区域的边界区域,我认为您的建议是有道理的,我可以帮助您了解那里发生的事情,您可以调整现有的 stackblitz链接它会非常感谢,我已经在上面一步一步解释了
  • 因此对于拖放,有可拖动和可放置指令,可移动和可移动区域指令用于维护灰色区域的 dropzone 指令内的边界,所有这些指令都通过可放置服务进行通信,所以这些是 6正在使用的指令,然后我添加了 Resize 指令,它也监听 mousemove 和 mouseup
  • 它可能看起来很多,但所有指令基本上都在听 mousedown、mousemove、mouseup :),所以我也尝试了与您在 Draggable 指令中建议的相同的行,因为它具有 dragstart正在使用子 div 顶部的帖子中的输出事件发射器(dragstart),因此在 Draggable 指令中的 dragstart 函数中,我试图检查调整大小类,并试图在调整大小类时停止拖动效果那里,但它不起作用,但你建议在 Resizable 指令上检查这个,希望现在你能帮助我,谢谢
  • @Enthu 您可以做得更好,因为您更了解您的代码,只需添加 Hostbinding-class 以在单击调整边框时调整大小并检查可拖动指令中是否存在调整大小的类。
  • 添加一个名为“调整大小”的类并在可拖动指令中检查该类
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
  • 2021-08-07
  • 1970-01-01
  • 2017-09-05
  • 1970-01-01
相关资源
最近更新 更多