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 {
}
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';
}
});
}
}
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','');
}
}
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 .
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é.