Как реализовать перетаскиваемый div в Angular 2 с использованием Rx
Я пытаюсь получить divgable div, используя Angular 2. Я использую этот пример из angular2 -examples repo в качестве отправной точки, только реально корректирует код для учета удаления метода toRx()
. Код работает, но он не учитывает события mouseout
. Это означает, что если я нажму на DivGable div и медленно перемещаю мышь, div будет двигаться с помощью мыши. Но если я слишком быстро перемещаю мышь, событие mouseout
отправляется вместо события mousemove
, а перетаскивание останавливается.
Как я могу сохранить перетаскивание после перемещения мыши до тех пор, пока не будет запущено событие mouseout
? Я попытался объединить поток событий mouseout
с mousemove
, так что события mouseout
обрабатываются как теги mousemove
, но это не работает.
Я использую 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 {
}
Ответы
Ответ 1
Я нашел ответ на этот вопрос в RxJs. Как работать с событиями документа. Суть проблемы в том, что события мыши отправляются только элементу, когда мышь находится над этим элементом. Поэтому мы хотим, чтобы событие mousedown
ограничивалось определенным элементом, но мы должны отслеживать глобальные события mousemove
и mouseup
. Вот новый код. Обратите внимание на использование декоратора @HostListener
на onMouseup
и onMousemove
указывает цель как document:mouseup
и document:mousemove
. Так глобальные события передаются в поток Rx.
официальная angular2 документация для HostListener не упоминает этот синтаксис target:eventName
, но эта старая документация по dart для 2.0.0-alpha.24 упоминает об этом. Кажется, он все еще работает в версии 2.0.0-beta.12.
@Directive({
selector: '[draggable]'
})
export class Draggable implements OnInit {
mouseup = new EventEmitter();
mousedown = new EventEmitter();
mousemove = new EventEmitter();
@HostListener('document: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('document:mousemove', ['$event'])
onMousemove(event) {
this.mousemove.emit(event);
}
constructor(public element: ElementRef) {
this.element.nativeElement.style.position = 'relative';
this.element.nativeElement.style.cursor = 'pointer';
map;
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';
}
});
}
}
Ответ 2
Вы можете использовать это: 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','');
}
}
Ответ 3
Вы можете создать большой div, который покрывает экранную недвижимость. Для начала этот div имеет более низкий индекс z, чем div, который вы хотите перетащить. При получении mousedown вы изменяете z-индекс div, чтобы быть выше, чем элемент перетаскивания, и получать события перемещения мыши в этом div. Вы можете использовать n, чтобы вычислить положение элемента drag-element. Затем вы можете остановить и отправить div обратно, когда вы получите мышь вверх.
Недавно я написал модульную структуру перетаскивания в Angular2. Пожалуйста, попробуйте и сообщите нам отзывы.
https://github.com/ivegotwings/ng2Draggable
Тем не менее, я останавливаю перетаскивание после запуска события mouseout.
Ответ 4
У меня такая же проблема с перетаскиваемым всплывающим окном, поэтому я добавляю события mousemove и mouseup для документирования на mousedown и удаляю их на mouseup.
Я использую Eric Martinez answer для динамического добавления и удаления прослушивателя событий.
Шаблон:
<div class="popup-win" (mousedown)="mousedown($event)"></div>
компонент:
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();
}
Здесь приведен пример использования Plunker.