How to implement a draggable div in Angular 2 using Rx

前端 未结 4 437
醉梦人生
醉梦人生 2020-12-24 07:43

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 cod

相关标签:
4条回答
  • 2020-12-24 08:08

    You could create a large div that covers the screen real estate. To start with this div has a lower z-index than the div you want to drag. On receiving mousedown you change the z-index of the div to be higher than the drag-element and receive mouse move events on this div. You could the n use that to compute the position of the drag-element. You can then stop and send the div back again when you receive a mouse up.

    I have recently written a modular drag and drop framework in Angular2. Please give it a try and provide feedback.

    https://github.com/ivegotwings/ng2Draggable

    However, I stop the drag once the mouseout event is fired.

    0 讨论(0)
  • 2020-12-24 08:23

    You can use this : npm install ng2draggable

    Use [ng2-draggable]="true", don't forget the ="true"

    You can find it here

    https://github.com/cedvdb/ng2draggable

    Here is the 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','');
            }
    }
    
    0 讨论(0)
  • 2020-12-24 08:31

    I have same problem with draggable popup, so I add mousemove and mouseup events to document on mousedown, and remove them on mouseup. I use Eric Martinez's answer for add and remove event listener dynamically.

    Template:

    <div class="popup-win" (mousedown)="mousedown($event)"></div>
    

    Component:

    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();
    }
    

    Here's a runnable example on Plunker.

    0 讨论(0)
  • 2020-12-24 08:32

    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';
                }
            });
        }
    }
    
    0 讨论(0)
提交回复
热议问题