美文网首页
为nzModal增加可拖拽功能 最后是RxJS实现版

为nzModal增加可拖拽功能 最后是RxJS实现版

作者: 4f4e62418dff | 来源:发表于2018-09-03 18:32 被阅读0次

实现功能很简单,就是nzModal像windows里的窗口一样,可以拖拽窗口以移动窗口位置。

思路也很简单。创建一个Directive,在Directive中先获取到modal-title的ElementRef对象和nz-modal的ElementRef。然后通过Render2为 modal-title 添加鼠标事件,再通过Render2修改nz-modal的style属性,达到跟随鼠标拖拽而移动的效果。

先创建一个新的Directive,并且引入ElementRef和Render2

import { Directive,ElementRef,Renderer2,AfterViewInit } from '@angular/core';

@Directive({
  selector: '[zmMovableModal]'
})
export class ZmMovableModalDirective {

  constructor(private elementRef:ElementRef,private render:Renderer2) {
  }
  ngAfterViewInit(){
  }
}

在你要事件拖拽的nzModal中增加这个新的Directive

  <nz-modal zmMovableModal nzTitle="Modal1" nzMask="false" (nzOnCancel)="hideModals()" [nzVisible]="modal1Visible"></nz-modal>
获取Element

首先我们在ZmMovableModalDirective中添加获取Title 和 nzModal的方法:

getModalElement(){
      return this.elementRef.nativeElement.querySelector('.ant-modal');
  }
  getModalTitleElment(){
    return this.elementRef.nativeElement.querySelector('.ant-modal-header');
  }

在ngAfterViewInit()里执行。

ngAfterViewInit(){
  let modalElement = this.getModalElement();
  let modalTitleElement = this.getModalTitleElment();
  console.log(modalElement)
  console.log(modalTitleElement);
}

如果控制台成功的打印出了对应的html代码段,就代表成功获取到了title和modal
如果是空的,可能是nzModal版本升级导致类名发生了变动,可以确定querySelector函数对应的class是否正确。

参数准备

在获取到title 和modal 后,我们先为实现拖拽功能准备一些参数:

//只有当鼠标点下之后,鼠标抬起之前,才能移动
private canMove :boolean = false;
//modal开始移动时的x,y坐标
  private modalX : number = 0;
  private modalY : number = 0;
//鼠标点下时,鼠标所在的坐标
  private mouseDownX :number = 0;
  private mouseDownY :number = 0;

有了这些准备后,我们开始为title添加鼠标事件。

鼠标事件

首先是鼠标点下的事件:

this.render.listen(modalTitleElement,'mousedown',function(event){
//记录modal和鼠标点击的起始坐标
      this.mouseDownX = event.clientX;
      this.mouseDownY = event.clientY;
//offsetLeft和offsetTop是相对窗口的坐标。
//我们的modal的移动正好也是基于整个窗口的。所以实现起来很简单。
      this.modalX = modalElement.offsetLeft;
      this.modalY = modalElement.offsetTop;
//将modal改为绝对定位。并且根据现在窗口的位置设置left和top
      this.render.setStyle(modalElement,"position","absolute");
      this.render.setStyle(modalElement,"top",`${this.modalY}px`);
      this.render.setStyle(modalElement,"left",`${this.modalX}px`);
//一切都准备好了 将状态设置 可移动。
      this.canMove = true;
}.bind(this));

然后设置mousemove的事件:

this.render.listen(modalTitleElement,'mousemove',function(event){
//只有在camMove状态下,才进行处理
      if(this.canMove){
//获得当前鼠标位置,并根据开始拖拽时的位置,计算出鼠标的偏移量
        let moveX = event.clientX - this.mouseDownX;
        let moveY = event.clientY - this.mouseDownY;
//鼠标的偏移量就是modal的偏移量。
//通过开始是modal的位置和偏移量计算Modal新的坐标
        let newModalX = this.modalX + moveX;
        let newModalY = this.modalY + moveY;
//将新坐标设置下去,这样modal就跟着鼠标移动了。
        this.render.setStyle(modalElement,"top",`${newModalY}px`);
        this.render.setStyle(modalElement,"left",`${newModalX}px`);
      }
    }.bind(this));

最后,是出挑抬起后的处理:

this.render.listen(modalTitleElement,'mouseup',function(event){
//将状态设置为不可移动
      this.canMove = false;
    }.bind(this));

这样nzModal拖拽的功能就实现了。

快速拖拽时产生BUG处理

虽然整体的功能实现了,但是还是存在一个问题。
在快速上下拖拽的时候,会有跟丢的现象。
应该是因为快速的鼠标飞出了title 的范围,那么加载在title上的mousemove事件就不会被调用,modal就不会移动了。
所以将mousemove事件绑定到更大的范围就处理了,
所以将

this.render.listen(modalTitleElement,'mousemove',function(event){
........

改成:

this.render.listen(this.elementRef.nativeElement,'mousemove',function(event){
............

无论怎么摇晃鼠标都不会跟丢了。

整体代码:
import { Directive,ElementRef,Renderer2,AfterViewInit } from '@angular/core';

@Directive({
  selector: '[zmMovableModal]'
})
export class ZmMovableModalDirective {
  private canMove :boolean = false;
  private modalX : number = 0;
  private modalY : number = 0;
  private mouseDownX :number = 0;
  private mouseDownY :number = 0;
  constructor(private elementRef:ElementRef,private render:Renderer2) {
  }
  ngAfterViewInit(){
    let modalElement = this.getModalElement();
    let modalTitleElement = this.getModalTitleElment();
    this.render.listen(modalTitleElement,'mousedown',function(event){
      this.mouseDownX = event.clientX;
      this.mouseDownY = event.clientY;
      this.modalX = modalElement.offsetLeft;
      this.modalY = modalElement.offsetTop;
      this.render.setStyle(modalElement,"position","absolute");
      this.render.setStyle(modalElement,"top",`${this.modalY}px`);
      this.render.setStyle(modalElement,"left",`${this.modalX}px`);
      this.canMove = true;
    }.bind(this));
    this.render.listen(modalTitleElement,'mouseup',function(event){
      this.canMove = false;
    }.bind(this));
   this.render.listen(this.elementRef.nativeElement,'mousemove',function(event){
      if(this.canMove){
        let moveX = event.clientX - this.mouseDownX;
        let moveY = event.clientY - this.mouseDownY;
        let newModalX = this.modalX + moveX;
        let newModalY = this.modalY + moveY;
        this.render.setStyle(modalElement,"top",`${newModalY}px`);
        this.render.setStyle(modalElement,"left",`${newModalX}px`);
      }
    }.bind(this));
  }
  getModalElement(){
      return this.elementRef.nativeElement.querySelector('.ant-modal');
  }
  getModalTitleElment(){
    return this.elementRef.nativeElement.querySelector('.ant-modal-header');
  }

}
新的问题

最近在使用中发现一个问题,就是如果nzTitle 是一个变量,那么虽然最后生成的html代码中有ant-modal-title这个div标签。但是在elementRef.nativeElement里却没相应的element。
这里还没找到原因。
想办先想办法绕过这个问题。

  • 1 将整个ant-modal-content作为拖拽的句柄。这个element肯定有的。
    但是有一个问题。如果modal中有文字需要复制的时候,你就无法通过鼠标选中文字进行复制。
  • 2 如果发现没有ant-modal-header,那么,向里面加入一个新的小element,作为拖拽的句柄。
    这样做的问题是操作不一致。会让用户产生疑惑。

最好还是能找到ant-modal-header element的根本原因是什么,从根本上解决问题。

将getModalTitleElement()方法代码改成如下代码,解决了这个问题:

  getModalTitleElment(){
    // let header = this.elementRef.nativeElement.querySelector('.ant-modal-header');
    let element = document.createElement("div") as any;
    // this.render.setStyle(element,"background","orange");
    this.render.setStyle(element,"width","100%");
    this.render.setStyle(element,"height","60px");
    this.render.setStyle(element,"position","absolute");
    this.render.setStyle(element,"top","0");
    this.render.setStyle(element,"left","0");
    this.render.appendChild(this.modalElement,element);
    return element;
  }

这样,只要用户拖动最上方的一段都可以拖拽整个模态框了。

使用RxJS

最近深入学习一些RxJS的用法。正好拿这个指令练练手。
获取element的方法不用改变。
在ngAfterViewInit中

let modalElement = this.getModalElement();
tihs.modalElement = modalElement;
let modalTitleElement = this.getModalTitleElement();
const mouseDown = Observable.fromEvent<MouseEvent>(modalTitleElement,"mousedown");
const mouseUp = Observable.fromEvent<MouseEvent>(this.elementRef.nativeElement,"mouseup");
const mouseMove = Observable.fromEvent<MouseEvent>(this.elementRef.nativeElement,"mousemove");
mouseDown.subscrible(event=>{
  this.mouseDownX = event.clentX;
  this.mouseDownY = event.clentY;
  this.modalX = modalElement.offsetLeft;
  this.modalY = modalElement.offsetTop;
  this.render.setStyle(modalElement,"position","absolute");
  this.render.setStyle(modalElement,"top",`${this.modalY}px`);
  this.render.setStyle(modalElelment,"left",`${this.mdoalX}px`);
});
mouseDown.map(event=>mouseMove.takeUntil(mouseUp))
.concatAll()
.subscribe(e=>{
  let moveX = e.clientX - this.mouseDownX;
  let moveY = e.clientY - this.mouseDownY;
  let newModalX = this.modalX + moveX;
  let newModalY = this.modalY + moveY;
  this.render.setStyle(modalElement,"top",`${newModalY}px`);
  this.render.setStyle(modalElement,"left",`${newModalX}px`);
});

不知道有什么方法可以用一串RxJS代码就实现整个功能。未来想到办法会更新上去。

新的解决方案

将上面代码的两个RxJS代码块合并为一个:

mouseDown.map(event=>{
  this.modalX = modalElement.offsetLeft;
  this.modalY = modalElement.offsetTop;
  this.render.setStyle(modalElment,"position","absolute");
  this.render.setStyle(modalElement,"top",`${this.modalY}px`);
  this.render.setStyle(modalElement,"left",`$(this.modalX)px`);
}).map(event=>mouseMove.takeUntil(mouseUp))
.contactAll()
.withLatestFrom(mouseDown,(move,down)=>{
  return {x:move.clientX-down.clientX,y:move.clientY-down.clientY}
})
.subscribe(pos=>{
  this.render.setStyle(modalElement,"top",`${this.modalY+pos.y}px`);
  this.render.setStyle(modalElement,"left",`$(this.modalX+pos.x)px`);
})

相关文章

  • 为nzModal增加可拖拽功能 最后是RxJS实现版

    实现功能很简单,就是nzModal像windows里的窗口一样,可以拖拽窗口以移动窗口位置。 思路也很简单。创建一...

  • POS-2017

    拖拽排序功能 实现方法: 使用jquery的Sortable功能可以实现拖拽功能 index页面 html部分 商...

  • JS实现拖拽功能

    拖拽功能是我们日常项目中常用的效果,今天我们就来研究一下如何实现简单的拖拽功能。想实现拖拽功能其实很简单,主要需要...

  • HTML5 拖拽事件

    HTML5 拖拽事件图片自带拖拽功能其他元素也通过设置 draggable=true属性 实现拖拽功能 垃圾(拖拽...

  • vdesjs实现原理

    拖拽功能实现 vdesjs的拖拽功能使用到了vuedraggble,vuedragable的实现是基于sortab...

  • 利用转场动画来实现类似QQ语音通话界面隐藏和出现的效果

    浮动可拖拽按钮功能的实现可以参考前一篇文章。利用手势拖拽控件并设置拖拽范围.接下来利用转场动画来实现类似QQ语音通...

  • 原生js实现可拖拽功能

    原生js实现一个可全局拖拽的按钮功能,直接上代码*js部分 *html部分 *css部分

  • Vue.Draggable学习总结

    Draggable为基于Sortable.js的vue组件,用以实现拖拽功能。 特性 支持触摸设备支持拖拽和选择文...

  • 百度地图web--拖拽选址

    实现地图拖拽选址功能,百度地图并未像高德地图拖拽选址功能 。由于项目需要,在百度地图的基础上实现简单的拖拽功能。大...

  • React 实现组件拖拽功能

    ##实现React组件的拖拽功能 HTML5原生支持拖拽功能,没有看过相关文档,恰好自己想实现一下React组件...

网友评论

      本文标题:为nzModal增加可拖拽功能 最后是RxJS实现版

      本文链接:https://www.haomeiwen.com/subject/gleiwftx.html