制作一个ngb draggable模态

时间:2017-08-23 08:25:44

标签: angular bootstrap-modal twitter-bootstrap-4 ng-bootstrap

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

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

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

提前致谢。

4 个答案:

答案 0 :(得分:3)

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

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

我最常使用的是第一个,因为它仅在单击模态时添加了mousemove侦听器,并在不再需要时删除它。此外,它运行角度以外的所有运动功能,因此拖动模态不会无缘无故地触发角度的变化检测(模态框内的任何内容都不会在被拖动时发生变化,我怀疑,因此无需检查)。然后,由于我的大多数模态都是在关闭时动态创建并销毁的,因此在这种情况下它们也可以删除事件侦听器。我注入了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);
   }
 } 
}

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

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

答案 1 :(得分:1)

您可以使用NPM软件包jquery和jqueryui

npm install --save jquery jqueryui

然后,它就像执行以下操作一样简单。

添加导入:

import * as $ from 'jquery';
import 'jqueryui';

或使用

declare var $: any;

将以下内容添加到您的ngOnInit()方法中:

$(document).ready(function(){
  let modalContent: any = $('.modal-content');
  let modalHeader = $('.modal-header');
  modalHeader.addClass('cursor-all-scroll');
  modalContent.draggable({
      handle: '.modal-header'
  });
});

**注意,我有一个用于更改光标的类,我只希望模式的标题成为拖动的句柄。

DEMO

答案 2 :(得分:1)

您可以使用angular2-draggable软件包。对我来说很好。

https://github.com/xieziyu/angular2-draggable

答案 3 :(得分:0)

我没有太多时间,所以我只会告诉你我刚才所做的事情。只需查看ts文件中的css和HostListeners。

HTML:

<div class="pdf-container">
  <div #pdfWrapper class="pdf-wrapper" [ngClass]="{'active': pdf}">
    <canvas #pdfCanvas [ngStyle]="{'pointer-events': pdf ? 'all' : 'none'}"></canvas>
    <span (click)="removePDF()" class="btn-pdf pdf-close" *ngIf="pdf" [ngStyle]="{'pointer-events': pdf ? 'all' : 'none'}">X</span>
    <span (click)="previousPage()" *ngIf="pdf && currentPage > 1" class="btn-pdf pdf-previous" [ngStyle]="{'pointer-events': pdf ? 'all' : 'none'}"><</span>
    <span (click)="nextPage()" *ngIf="pdf && currentPage < numPages" class="btn-pdf pdf-next" [ngStyle]="{'pointer-events': pdf ? 'all' : 'none'}">></span>
  </div>
</div>

样式:

.pdf-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.pdf-wrapper {
  position: relative;
  width: 450px;
  height: 600px;
}
.pdf-wrapper.active {
  box-shadow: 0 0 5px black;
  cursor: move;
}
canvas {
  position: absolute;
  width: inherit;
  height: inherit;
}
.btn-pdf {
  position: absolute;
  width: 20px;
  height: 20px;
  background-color: rgba(0, 0, 0, 0.5);
  color: white;
  text-align:center;
  cursor: pointer;
}
.btn-pdf.pdf-close {
  right: 0;
}
.btn-pdf.pdf-previous {
  top: 50%;    
}
.btn-pdf.pdf-next {
  right: 0;
  top: 50%
}

TS文件:

@ViewChild('pdfWrapper') pdfWrapper;
private mouseDown : boolean = false;
  private lastMouseDown;

@HostListener('mouseup')
  onMouseup() {
    this.mouseDown = false;
  }

  @HostListener('mousemove', ['$event'])
  onMousemove(event: MouseEvent) {
    if(this.mouseDown && this.pdf) {
      this.pdfWrapper.nativeElement.style.top = (event.clientY - this.lastMouseDown.offsetY) + 'px';
      this.pdfWrapper.nativeElement.style.left = (event.clientX - this.lastMouseDown.offsetX) + 'px';
    }

@HostListener('mousedown', ['$event'])
  onMousedown(event) {
    if (!this.mouseDown) {
      this.lastMouseDown = event;
    }
    this.mouseDown = true;

  }