else - Cómo implementar un div arrastrable en Angular 2 usando Rx
ngif angular 6 example (4)
He estado tratando de hacer funcionar un div arrastrable usando Angular 2. Estoy usando este ejemplo del repositorio de angulo2-ejemplos como punto de partida, solo ajustando realmente el código para tener en cuenta la eliminación del método toRx()
. El código funciona, pero no tiene en cuenta los eventos de mouseout
. Esto significa que si hago clic en un div Draggable y muevo el mouse lentamente , el div se moverá con el mouse. Pero si muevo el mouse demasiado rápido, se envía un evento de mousemove
lugar de un evento mousemove
, y el arrastre se detiene.
¿Cómo puedo mantener el arrastre después de mover el mouse hasta el momento en que se mouseout
un evento de mouseout
? He intentado fusionar el mouseout
eventos mouseout
con el mousemove
, para que los eventos mouseout
se traten como mousemove
, pero eso no funciona.
Estoy usando 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 {
}
Encontré la respuesta a esto en RxJs ¿Cómo lidiar con los eventos de documentos ? El quid del problema es que los eventos del mouse solo se envían a un elemento cuando el mouse está sobre ese elemento. Así que queremos que el evento de mousedown
se limite a un elemento específico, pero tenemos que hacer un seguimiento de los mousemove
globales de mousemove
y mouseup
. Aquí está el nuevo código. Note que el uso del decorador @HostListener
en onMouseup
y onMousemove
especifica el objetivo como document:mouseup
y document:mousemove
. Así es como se canalizan los eventos globales en el flujo de Rx.
La documentación oficial de angular2 para HostListener no menciona este target:eventName
sintaxis de target:eventName
, pero esta documentación antigua de dart para 2.0.0-alpha.24 sí lo menciona. Parece que todavía funciona 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'';
}
});
}
}
Puedes usar esto: npm install ng2draggable
Use [ng2-draggable]="true"
, no olvide el ="true"
Lo puedes encontrar aquí
https://github.com/cedvdb/ng2draggable
Aquí está el código:
@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'','''');
}
}
Tengo el mismo problema con la ventana emergente que se puede arrastrar, así que agrego los eventos mousemove y mouseup al documento en mousedown y los elimino en mouseup. Utilizo la respuesta de Eric Martínez para agregar y eliminar dinámicamente el detector de eventos.
Modelo:
<div class="popup-win" (mousedown)="mousedown($event)"></div>
Componente:
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();
}
Aquí hay un ejemplo ejecutable en Plunker .
Usted podría crear una gran división que cubre la pantalla de bienes raíces. Para empezar, este div tiene un índice z más bajo que el div que desea arrastrar. Al recibir mousedown, cambia el índice z del div para que sea más alto que el elemento de arrastre y recibe eventos de movimiento del mouse en este div. Puede usar n para calcular la posición del elemento de arrastre. Luego puede detener y enviar el div de nuevo cuando reciba un mouse arriba.
Recientemente he escrito un marco modular de arrastrar y soltar en Angular2. Por favor, pruébalo y proporciona comentarios.
https://github.com/ivegotwings/ng2Draggable
Sin embargo, detengo el arrastre una vez que se desencadena el evento mouseout.