web-dev-qa-db-fra.com

Comment implémenter un div glissable dans Angular 2 en utilisant Rx

J'ai essayé de faire fonctionner un div glissable en utilisant Angular 2. J'utilise cet exemple du repo angular2-examples comme point de départ, seulement vraiment ajuster le code pour tenir compte de la suppression de la méthode toRx(). Le code fonctionne, mais il ne prend pas en compte les événements mouseout. Cela signifie que si je clique sur un div Draggable et que je déplace la souris lentement , le div se déplacera avec la souris. Mais si je déplace la souris trop vite, un événement mouseout est envoyé à la place d'un événement mousemove et le glissement s'arrête.

Comment puis-je maintenir le glissement après le déplacement de la souris jusqu'à ce qu'un événement mouseout soit déclenché? J'ai essayé de fusionner le flux d'événements mouseout avec celui de mousemove, afin que les événements de mouseout soient traités comme les événements de mousemove, mais cela ne fonctionne pas travail.

J'utilise 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 {
}
18
Chris

J'ai trouvé la réponse à cela dans RxJs Comment gérer les événements de document . Le nœud du problème est que les événements de souris ne sont envoyés à un élément que lorsque la souris survole cet élément. Nous voulons donc que l'événement mousedown soit limité à un élément spécifique, mais nous devons suivre global mousemove et mouseup événements. Voici le nouveau code. Remarquez l'utilisation du @HostListener décorateur sur onMouseup et onMousemove spécifie la cible comme document:mouseup et document:mousemove. C'est ainsi que les événements globaux sont canalisés dans le flux Rx.

La documentation officielle angular2 pour HostListener ne mentionne pas ceci target:eventName syntaxe, mais cette ancienne documentation Dart pour 2.0.0-alpha.24 le mentionne. Il semble toujours fonctionner en 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';
            }
        });
    }
}
25
Chris

Vous pouvez utiliser ceci: npm install ng2draggable

Utilisation [ng2-draggable]="true", n'oubliez pas le ="true"

Vous pouvez le trouver ici

https://github.com/cedvdb/ng2draggable

Voici le code:

@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','');
        }
}
5
Ced

J'ai le même problème avec le popup déplaçable, donc j'ajoute des événements mousemove et mouseup au document sur mousedown et les supprime sur mouseup. J'utilise la réponse d'Eric Martinez pour ajouter et supprimer dynamiquement l'écouteur d'événements.

Modèle:

<div class="popup-win" (mousedown)="mousedown($event)"></div>

Composant:

constructor(private elementRef: ElementRef,
        private renderer: Renderer2) {}

mousedown(event: any) {
    this.xStartElementPoint = this.curX;
    this.yStartElementPoint = this.curY;
    this.xStartMousePoint = event.pageX;
    this.yStartMousePoint = event.pageY;
    this.mousemoveEvent = this.renderer.listen("document", "mousemove", this.dragging);
    this.mouseupEvent = this.renderer.listen("document", "mouseup", this.mouseup);
}

dragging(event: any) {
     this.curX = this.xStartElementPoint + (event.pageX - this.xStartMousePoint);
     this.curY = this.yStartElementPoint + (event.pageY - this.yStartMousePoint);
}
mouseup(event: any) {
    // Remove listeners
    this.mousemoveEvent();
    this.mouseupEvent();
}

Voici un exemple exécutable sur Plunker .

1
ali-myousefi

Vous pouvez créer une grande div qui couvre la surface de l'écran. Pour commencer, ce div a un index z inférieur à celui que vous souhaitez faire glisser. Lors de la réception de la souris, vous changez le z-index de la div pour qu'il soit plus élevé que l'élément de glissement et recevez les événements de déplacement de la souris sur cette div. Vous pourriez le n utiliser cela pour calculer la position de l'élément de glissement. Vous pouvez ensuite arrêter et renvoyer le div lorsque vous recevez une souris.

J'ai récemment écrit un framework modulaire de glisser-déposer dans Angular2. Veuillez essayer et fournir des commentaires.

https://github.com/ivegotwings/ng2Draggable

Cependant, j'arrête le glissement une fois que l'événement mouseout est déclenché.

1
shiv