如何使用Rx在Angular 2中实现可拖动div [英] How to implement a draggable div in Angular 2 using Rx
问题描述
我一直在尝试使用Angular 2使用可拖动的div.我正在使用
I've been trying to get a draggable div working using Angular 2. I'm using this example from the angular2-examples repo as a starting point, only really adjusting the code to account for the removal of the toRx()
method. The code works, but it does not account for mouseout
events. This means that if I click on a Draggable div, and move the mouse slowly, the div will move with the mouse. But if I move the mouse too fast, a mouseout
event is sent instead of a mousemove
event, and the dragging stops.
如何将鼠标移动到足以触发mouseout
事件的位置,使拖动继续进行?我试过将mouseout
事件流与mousemove
合并,以便将mouseout
事件与mousemove
一样对待,但这是行不通的.
How can I keep the drag going after the mouse is moved so far that a mouseout
event is fired? I've tried merging the mouseout
event stream with the mousemove
one, so that mouseout
events are treated just like mousemove
ones, but that doesn't work.
我正在使用Angular 2.0.0-beta.12.
I'm using 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 {
}
推荐答案
I found the answer to this in RxJs How do deal with document events. The crux of the problem is that mouse events are only sent to an element when the mouse is over that element. So we do want the mousedown
event limited to specific element, but we have to track global mousemove
and mouseup
events. Here's the new code. Notice the use of the @HostListener
decorator on onMouseup
and onMousemove
specifies the target as document:mouseup
and document:mousemove
. This is how the global events are piped into the Rx stream.
HostListener的官方angular2文档不会"不要提及此target:eventName
语法,但
The official angular2 documentation for HostListener doesn't mention this target:eventName
syntax, but this old dart documentation for 2.0.0-alpha.24 does mention it. It seems to still work in 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';
}
});
}
}
这篇关于如何使用Rx在Angular 2中实现可拖动div的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!