制作ngb可拖动模式 [英] Making a ngb draggable modal

查看:49
本文介绍了制作ngb可拖动模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Angular应用程序,其中使用 ng-bootstrap 显示模式.

I have an Angular application in which i use ng-bootstrap to display a modal.

我的问题是ngb团队不支持拖动这些模式,并且显然没有任何计划这样做

My issue is that the ngb team doesn't support dragging these modals and apparently has no plans of doing so any time soon.

所以我的问题是:谁能告诉我如何使这种模式可拖动?

So my question is: Can anyone tell me how i can make such a modal draggable?

谢谢.

推荐答案

这是我所写的-有很多不必要的东西,它们只是在尽可能遵循性能最佳做法的同时降低性能的每一点-我将以精简/简化版本跟进.

Here is the one I have written - there's a lot of unnecessary stuff that's there just to squeak out every bit of performance as possible while also following angular best practices - i'll follow it with a trimmed down / simplified version.

它们都只需要指令,除了将指令选择器添加到所需元素上之外,无需更改CSS或html/查询.他们俩都使用translation3d而不是更改顶部和左侧位置,这可以在某些浏览器上触发GPU加速,但是即使没有此操作,它通常也比改变位置更平滑.它是进行转换转换的目的-相对于iself移动元素.它们都使用HostBinding绑定到属性,而不是直接访问nativeElement属性(不必要地将指令耦合到DOM).第二个很好,因为它不需要ElementRef或Renderer2的依赖关系,但是它向文档对象添加了一个始终在线的侦听器,因此即使它看上去更干净,我也很少使用它.

They both only need the directive alone, no css or html changes / queries other than adding the directive selector to the element you want it on. They both use translate3d rather than changing top and left position, which can trigger GPU acceleration on certain browsers, but even without that it usually just performs smoother than changing position anyway. Its what the translate transform was made for - moving an element relative to iself. They both use HostBinding to bind to the property, rather than directly accessing a nativeElement attribute (which unnecessarily couples the directive to the DOM). The second one is nice bc it doesn't require dependencies for ElementRef or Renderer2, but it adds an always-on listener to the document object so i rarely use it, even though its cleaner looking.

我最常使用第一个,因为它仅在单击模式时才添加mousemove侦听器,并在不再需要它时将其删除.此外,它在angular之外运行所有运动功能,因此拖动模态不会无缘无故地触发Angle的变化检测(我怀疑,在拖动其时,modal框内的任何内容都不会改变,因此无需检查).然后,由于我的大多数模式都是动态创建的,并且在关闭时会销毁,因此在这种情况下,它们也可以删除事件侦听器.我注入elementref,这样我就可以获得对指令的父元素的引用,该引用需要访问nativeElement,但是我实际上并没有修改值,只读取了一次即可获得引用.所以我认为它在Angular教义中是可以原谅的:p

I use the first one most because it only adds the mousemove listener when the modal is clicked, and removes it when its no longer needed. Additionally, it runs all of the movement functions outside of angular so dragging the modal won't constantly be triggering angular's change detection for no reason (nothing inside the modal box will be changing while its being dragged, i suspect, so its unnecessary to check). And then, since most of my modals are created dynamically and destroyed when they are closed, they can also remove event listeners in that case. I inject elementref so I can get a reference to the directive's parent element which requires accessing nativeElement, but I'm not actually modifying the values, just reading them once to get a reference. So I think its forgiveable in Angular doctrine :p

import { Directive,
         Input,
         NgZone,
         Renderer2,
         HostListener,
         HostBinding,
         OnInit, 
         ElementRef } from '@angular/core';

class Position {
   x: number; y: number;
   constructor (x, y) { this.x = x; this.y = y; }
};

@Directive({
  selector: '[lt-drag]'
})
export class LtDragDirective {

   private allowDrag = true;
   private moving = false;
   private origin = null;

   // for adding / detaching mouse listeners dynamically so they're not *always* listening
   private moveFunc: Function;
   private clickFunc: Function;

   constructor(private el:ElementRef,
               private zone: NgZone,
               private rend: Renderer2 ) {}

   @Input('handle') handle: HTMLElement; 

   @HostBinding('style.transform') transform: string = 'translate3d(0,0,0)'; 

   ngOnInit() {  

   let host = this.el.nativeElement.offsetParent; 

   // applies mousemove and mouseup listeners to the parent component, typically my app componennt window, I prefer doing it like this so I'm not binding to a window or document object

   this.clickFunc = this.rend.listen(host, 'mouseup' , ()=>{
     this.moving = false;
   });

    // uses ngzone to run moving outside angular for better performance
    this.moveFunc = this.rend.listen(host, 'mousemove' ,($event)=>{
      if (this.moving && this.allowDrag) {
        this.zone.runOutsideAngular(()=>{
           event.preventDefault();
           this.moveTo($event.clientX, $event.clientY);
        }); 
     }
  });
} 

 // detach listeners if host element is removed from DOM
ngOnDestroy() { 
   if (this.clickFunc ) { this.clickFunc(); }
   if (this.moveFunc )  { this.moveFunc();  }
}

 // parses css translate string for exact px position
 private getPosition(x:number, y:number) : Position {
    let transVal:string[] = this.transform.split(',');
    let newX = parseInt(transVal[0].replace('translate3d(',''));
    let newY = parseInt(transVal[1]);
    return new Position(x - newX, y - newY);
 }

 private moveTo(x:number, y:number) : void {
    if (this.origin) {
       this.transform = this.getTranslate( (x - this.origin.x), (y - this.origin.y) );
    }
 }

 private getTranslate(x:number,y:number) : string{
    return 'translate3d('+x+'px,'+y+'px,0px)';
 }

  @HostListener('mousedown',['$event'])
  onMouseDown(event: MouseEvent) {
    if (event.button == 2 || (this.handle !== undefined && event.target !== 
   this.handle)) {
     return;
    }
    else {
     this.moving = true;
     this.origin = this.getPosition(event.clientX, event.clientY);
   }
 } 
}

下面的简单版本-如果您不希望保持事件侦听器打开或绑定到文档对象

simpler version below -- if you're not concerned with keeping event listeners open or binding to the document object

 import { Directive,
         Input, 
         HostListener,
         HostBinding,
         OnInit  } from '@angular/core';

 class Position {
   x: number; y: number;
   constructor (x, y) { this.x = x; this.y = y; }
 };

@Directive({
   selector: '[lt-drag]'
})
export class LtDragDirective {

     private moving = false;
    private origin = null;

   constructor( ) {}

    @Input('handle') handle: HTMLElement; 

    @HostBinding('style.transform') transform: string = 'translate3d(0,0,0)'; 

    @HostListener('document:mousemove',[$event]) mousemove($event:MouseEvent) {
       event.preventDefault();
       this.moveTo($event.clientX, $event.clientY);
   }

    @HostListener('document:mouseup') mouseup() { 
        this.moving = false;
   }

    @HostListener('mousedown',['$event'])
   onMouseDown(event: MouseEvent) {
      if (event.button == 2 || (this.handle !== undefined && event.target     !== this.handle)) {
     return;   // if handle was provided and not clicked, ignore
     }
     else {
        this.moving = true;
        this.origin = this.getPosition(event.clientX, event.clientY);
   }
 }
  private getPosition(x:number, y:number) : Position {
     let transVal:string[] = this.transform.split(',');
     let newX = parseInt(transVal[0].replace('translate3d(',''));
     let newY = parseInt(transVal[1]);
     return new Position(x - newX, y - newY);
  }

   private moveTo(x:number, y:number) : void {
      if (this.origin) {
        this.transform = this.getTranslate( (x - this.origin.x), (y -  
        this.origin.y) );
      }
   }

   private getTranslate(x:number,y:number) : string{
      return 'translate3d('+x+'px,'+y+'px,0px)';
   }
 }

这篇关于制作ngb可拖动模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆