如何使用 Rx 在 Angular 2 中实现可拖动的 div [英] How to implement a draggable div in Angular 2 using Rx
问题描述
我一直在尝试使用 Angular 2 获得可拖动的 div.我正在使用 这个例子来自angular2-examples repo 作为起点,只是真正调整代码以解决toRx()
方法.该代码有效,但它不考虑 mouseout
事件.这意味着如果我点击一个 Draggable div,然后移动鼠标 缓慢,div 将随鼠标移动.但是,如果我将鼠标移动得太快,则会发送 mouseout
事件而不是 mousemove
事件,并且拖动停止.
如何在鼠标移动到触发 mouseout
事件后继续拖动?我尝试将 mouseout
事件流与 mousemove
合并,这样 mouseout
事件就像 mousemove
> 那些,但这不起作用.
我使用的是 Angular 2.0.0-beta.12.
从'angular2/core'导入{Component, Directive, HostListener, EventEmitter, ElementRef, OnInit};从'rxjs/Rx'导入{地图,合并};@指示({选择器:'[可拖动]'})导出类 Draggable 实现 OnInit {mouseup = new EventEmitter();mousedown = new EventEmitter();mousemove = new EventEmitter();mouseout = new EventEmitter();@HostListener('mouseup', ['$event'])onMouseup(事件){this.mouseup.emit(事件);}@HostListener('mousedown', ['$event'])onMousedown(事件){this.mousedown.emit(事件);返回假;//在事件上调用 preventDefault()}@HostListener('mousemove', ['$event'])onMousemove(事件){this.mousemove.emit(事件);}@HostListener('mouseout', ['$event'])onMouseout(事件){this.mouseout.emit(事件);返回假;//在事件上调用 preventDefault()}构造函数(公共元素:ElementRef){this.element.nativeElement.style.position = 'relative';this.element.nativeElement.style.cursor = '指针';地图;合并;this.mousedrag = this.mousedown.map(event => {返回 {顶部: event.clientY - this.element.nativeElement.getBoundingClientRect().top左:event.clientX - this.element.nativeElement.getBoundingClientRect().left,};}).flatMap(图像偏移 =>this.mousemove.merge(this.mouseout).map(pos => ({顶部:pos.clientY - imageOffset.top,左:pos.clientX - imageOffset.left})).takeUntil(this.mouseup));}ngOnInit() {this.mousedrag.subscribe({下一个: pos =>{this.element.nativeElement.style.top = pos.top + 'px';this.element.nativeElement.style.left = pos.left + 'px';}});}}@成分({选择器:'我的应用',模板:`<div可拖动><h1>你好,世界!</h1>
`,指令:[可拖动,],})导出类 AppComponent {}
我在 RxJs 如何处理文档事件.问题的关键是鼠标事件仅在鼠标悬停在该元素上时才会发送到该元素.所以我们确实希望 mousedown
事件仅限于特定元素,但我们必须跟踪 global mousemove
和 mouseup
事件.这是新代码.注意在 onMouseup
和 onMousemove
上使用 @HostListener
装饰器将目标指定为 document:mouseup
和 文档:鼠标移动
.这就是全局事件通过管道传输到 Rx 流的方式.
HostListener 的官方 angular2 文档t 提到这个 target:eventName
语法,但是 2.0.0-alpha.24 的旧 dart 文档 确实提到了它.它似乎仍然适用于 2.0.0-beta.12.
@Directive({选择器:'[可拖动]'})导出类 Draggable 实现 OnInit {mouseup = new EventEmitter();mousedown = new EventEmitter();mousemove = new EventEmitter();mousedrag: Observable<{top, left}>;@HostListener('document:mouseup', ['$event'])onMouseup(事件:MouseEvent){this.mouseup.emit(事件);}@HostListener('mousedown', ['$event'])onMousedown(事件:MouseEvent){this.mousedown.emit(事件);返回假;//在事件上调用 preventDefault()}@HostListener('document:mousemove', ['$event'])onMousemove(事件:MouseEvent){this.mousemove.emit(事件);}构造函数(公共元素:ElementRef){this.element.nativeElement.style.position = 'relative';this.element.nativeElement.style.cursor = '指针';this.mousedrag = this.mousedown.map(event => {返回 {顶部: event.clientY - this.element.nativeElement.getBoundingClientRect().top左:event.clientX - this.element.nativeElement.getBoundingClientRect().left,};}).flatMap(图像偏移 =>this.mousemove.map(pos => ({顶部:pos.clientY - imageOffset.top,左:pos.clientX - imageOffset.left})).takeUntil(this.mouseup));}ngOnInit() {this.mousedrag.subscribe({下一个: pos =>{this.element.nativeElement.style.top = pos.top + 'px';this.element.nativeElement.style.left = pos.left + 'px';}});}}
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.
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.
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.
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屋!