制作一个 ngb 可拖动的模态

met*_*ard 7 bootstrap-modal twitter-bootstrap-4 ng-bootstrap angular

我有一个 Angular 应用程序,我在其中使用ng-bootstrap显示一个modal

我的问题是 ngb 团队不支持拖动这些模态,而且显然没有计划很快这样做。

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

提前致谢。

dio*_*ide 5

这是我写的一个 - 有很多不必要的东西只是为了尽可能地提高每一点性能,同时也遵循有角度的最佳实践 - 我将遵循它的精简/简化版本。

他们都只需要单独的指令,除了将指令选择器添加到您想要的元素之外,不需要 css 或 html 更改/查询。他们都使用 translate3d 而不是改变顶部和左边的位置,这可以在某些浏览器上触发 GPU 加速,但即使没有它,它通常也比改变位置更平滑。它是平移变换的目的 - 相对于自身移动元素。它们都使用 HostBinding 绑定到属性,而不是直接访问 nativeElement 属性(这会不必要地将指令耦合到 DOM)。第二个很好,因为它不需要 ElementRef 或 Renderer2 的依赖项,但它向文档对象添加了一个永远在线的侦听器,所以我很少使用它,即使它看起来更干净。

我最常使用第一个,因为它仅在单击模态时添加 mousemove 侦听器,并在不再需要时将其删除。此外,它运行 angular 之外的所有移动功能,因此拖动模态不会无缘无故地不断触发 angular 的变化检测(我怀疑,模态框内的任何内容在被拖动时都不会发生变化,因此没有必要检查)。然后,由于我的大多数模态都是动态创建的,并在关闭时销毁,因此在这种情况下,它们还可以删除事件侦听器。我注入 elementref 以便我可以获得对需要访问 nativeElement 的指令的父元素的引用,但我实际上并没有修改这些值,只是读取它们一次以获取引用。所以我认为它在 Angular 学说中是可以原谅的: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);
   }
 } 
}
Run Code Online (Sandbox Code Playgroud)

下面更简单的版本——如果你不关心保持事件侦听器打开或绑定到文档对象

 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)';
   }
 }
Run Code Online (Sandbox Code Playgroud)