22

私は Angular 2 を使用してドラッグ可能な div を機能させようとしています。Angular2-examples リポジトリのこの例をtoRx()出発点として使用していますが、メソッドの削除を考慮してコードを実際に調整しているだけです。mouseoutコードは機能しますが、イベントを考慮していません。これは、ドラッグ可能な div をクリックしてマウスをゆっくり動かすと、div がマウスと共に移動することを意味します。しかし、マウスを速く動かしすぎると、mouseoutイベントではなくイベントが送信されmousemove、ドラッグが停止します。

mouseoutイベントが発生するまでマウスを動かした後、ドラッグを継続するにはどうすればよいですか? イベントがイベントと同じように扱われるように、mouseoutイベント ストリームをmousemove1 つにマージしようとしましたが、うまくいきません。mouseoutmousemove

Angular 2.0.0-beta.12 を使用しています。

import {Component, Directive, HostListener, EventEmitter, ElementRef, OnInit} from 'angular2/core';
import {map, merge} from 'rxjs/Rx';

@Directive({
    selector: '[draggable]'
})
export class Draggable implements OnInit {

    mouseup = new EventEmitter();
    mousedown = new EventEmitter();
    mousemove = new EventEmitter();
    mouseout = new EventEmitter();

    @HostListener('mouseup', ['$event'])
    onMouseup(event) {
        this.mouseup.emit(event);
    }

    @HostListener('mousedown', ['$event'])
    onMousedown(event) {
        this.mousedown.emit(event);
        return false; // Call preventDefault() on the event
    }

    @HostListener('mousemove', ['$event'])
    onMousemove(event) {
        this.mousemove.emit(event);
    }

    @HostListener('mouseout', ['$event'])
    onMouseout(event) {
        this.mouseout.emit(event);
        return false; // Call preventDefault() on the event
    }

    constructor(public element: ElementRef) {
        this.element.nativeElement.style.position = 'relative';
        this.element.nativeElement.style.cursor = 'pointer';

        map;
        merge;
        this.mousedrag = this.mousedown.map(event => {
            return {
                top: event.clientY - this.element.nativeElement.getBoundingClientRect().top
                left: event.clientX - this.element.nativeElement.getBoundingClientRect().left,
            };
        })
        .flatMap(
            imageOffset => this.mousemove.merge(this.mouseout).map(pos => ({
                top: pos.clientY - imageOffset.top,
                left: pos.clientX - imageOffset.left
            }))
            .takeUntil(this.mouseup)
        );
    }

    ngOnInit() {
        this.mousedrag.subscribe({
            next: pos => {
                this.element.nativeElement.style.top = pos.top + 'px';
                this.element.nativeElement.style.left = pos.left + 'px';
            }
        });
    }
}

@Component({
    selector: 'my-app',
    template: `
        <div draggable>
            <h1>Hello, World!</h1>
        </div>
        `,
    directives: [Draggable,],
})
export class AppComponent {
}
4

4 に答える 4

29

RxJs How do deal with document events でこれに対する答えを見つけました。問題の核心は、マウスが要素の上にあるときにのみ、マウス イベントが要素に送信されることです。mousedownしたがって、イベントを特定の要素に限定したいのですが、グローバル mousemoveイベントとmouseupイベントを追跡する必要があります。これが新しいコードです。@HostListenerデコレータ ononMouseupを使用しonMousemoveて、ターゲットをdocument:mouseupandとして指定していることに注意してくださいdocument:mousemove。これは、グローバル イベントが Rx ストリームにパイプされる方法です。

HostListenerの公式の angular2 ドキュメントでtarget:eventNameはこの構文について言及されていませんが、2.0.0-alpha.24 のこの古い dart ドキュメントでは言及されています。2.0.0-beta.12 でも動作するようです。

@Directive({
    selector: '[draggable]'
})
export class Draggable implements OnInit {

    mouseup = new EventEmitter<MouseEvent>();
    mousedown = new EventEmitter<MouseEvent>();
    mousemove = new EventEmitter<MouseEvent>();

    mousedrag: Observable<{top, left}>;

    @HostListener('document:mouseup', ['$event'])
    onMouseup(event: MouseEvent) {
        this.mouseup.emit(event);
    }

    @HostListener('mousedown', ['$event'])
    onMousedown(event: MouseEvent) {
        this.mousedown.emit(event);
        return false; // Call preventDefault() on the event
    }

    @HostListener('document:mousemove', ['$event'])
    onMousemove(event: MouseEvent) {
        this.mousemove.emit(event);
    }

    constructor(public element: ElementRef) {
        this.element.nativeElement.style.position = 'relative';
        this.element.nativeElement.style.cursor = 'pointer';

        this.mousedrag = this.mousedown.map(event => {
            return {
                top: event.clientY - this.element.nativeElement.getBoundingClientRect().top
                left: event.clientX - this.element.nativeElement.getBoundingClientRect().left,
            };
        })
        .flatMap(
            imageOffset => this.mousemove.map(pos => ({
                top: pos.clientY - imageOffset.top,
                left: pos.clientX - imageOffset.left
            }))
            .takeUntil(this.mouseup)
        );
    }

    ngOnInit() {
        this.mousedrag.subscribe({
            next: pos => {
                this.element.nativeElement.style.top = pos.top + 'px';
                this.element.nativeElement.style.left = pos.left + 'px';
            }
        });
    }
}
于 2016-03-29T17:10:24.120 に答える
5

これを使用できます: npm install ng2draggable

を使用[ng2-draggable]="true"し、忘れないでください="true"

ここで見つけることができます

https://github.com/cedvdb/ng2draggable

コードは次のとおりです。

@Directive({
  selector: '[ng2-draggable]'
})
export class Draggable implements OnInit{
    topStart:number=0;
    leftStart:number=0;
    _allowDrag:boolean = true;
    md:boolean;

    constructor(public element: ElementRef) {}

        ngOnInit(){
          // css changes
          if(this._allowDrag){
            this.element.nativeElement.style.position = 'relative';
            this.element.nativeElement.className += ' cursor-draggable';
          }
        }

        @HostListener('mousedown', ['$event'])
        onMouseDown(event:MouseEvent) {
          if(event.button === 2)
            return; // prevents right click drag, remove his if you don't want it
          this.md = true;
          this.topStart = event.clientY - this.element.nativeElement.style.top.replace('px','');
          this.leftStart = event.clientX - this.element.nativeElement.style.left.replace('px','');
        }

        @HostListener('document:mouseup')
        onMouseUp(event:MouseEvent) {
          this.md = false;
        }

        @HostListener('document:mousemove', ['$event'])
        onMouseMove(event:MouseEvent) {
          if(this.md && this._allowDrag){
            this.element.nativeElement.style.top = (event.clientY - this.topStart) + 'px';
            this.element.nativeElement.style.left = (event.clientX - this.leftStart) + 'px';
          }
        }

        @HostListener('touchstart', ['$event'])
        onTouchStart(event:TouchEvent) {
          this.md = true;
          this.topStart = event.changedTouches[0].clientY - this.element.nativeElement.style.top.replace('px','');
          this.leftStart = event.changedTouches[0].clientX - this.element.nativeElement.style.left.replace('px','');
          event.stopPropagation();
        }

        @HostListener('document:touchend')
        onTouchEnd() {
          this.md = false;
        }

        @HostListener('document:touchmove', ['$event'])
        onTouchMove(event:TouchEvent) {
          if(this.md && this._allowDrag){
            this.element.nativeElement.style.top = ( event.changedTouches[0].clientY - this.topStart ) + 'px';
            this.element.nativeElement.style.left = ( event.changedTouches[0].clientX - this.leftStart ) + 'px';
          }
          event.stopPropagation();
        }

        @Input('ng2-draggable')
        set allowDrag(value:boolean){
          this._allowDrag = value;
          if(this._allowDrag)
            this.element.nativeElement.className += ' cursor-draggable';
          else
            this.element.nativeElement.className = this.element.nativeElement.className
                                                    .replace(' cursor-draggable','');
        }
}
于 2016-10-13T16:53:18.533 に答える
1

画面の領域をカバーする大きな div を作成できます。まず、この div は、ドラッグする div よりも低い z-index を持っています。mousedown を受け取ると、div の z-index をドラッグ要素よりも高くなるように変更し、この div でマウス移動イベントを受け取ります。それを使用して、ドラッグ要素の位置を計算できます。その後、マウスアップを受け取ったときに、停止して div を再び送り返すことができます。

最近、Angular2 でモジュール式のドラッグ アンド ドロップ フレームワークを作成しました。ぜひお試しいただき、フィードバックをお寄せください。

https://github.com/ivegotwings/ng2Draggable

ただし、マウスアウト イベントが発生したら、ドラッグを停止します。

于 2016-03-29T02:01:37.890 に答える