美文网首页
react + 原生JS实现弹框拖拽解决方案

react + 原生JS实现弹框拖拽解决方案

作者: 南宫__ | 来源:发表于2018-12-18 20:48 被阅读0次

    前言

    项目github地址
    最近项目需要实现一个弹框拖拽的功能,本来多快好省的方案就是使用antd的弹框,再配合gayhub上找的拖拽插件来实现。
    但过程中遇到一些麻烦,而且用别的东西一时爽,要实现个性化或者改一些东西,需要看别人的源码,会极其的麻烦。
    这时,固执的程序员就会抛下一切,全程自己开发,比如我。正所谓求人不如求己,自己动手,丰衣足食。
    自己开发,时间可能会多花一些,但收益也是有目共睹的:
    1.风险可控,出了问题能快速定位并解决(毕竟是亲手制作);
    2.能最大程度实现个性化,比如增加或者减少一些功能;
    3.杜绝知其然而不知其所以然。通过手把手搭建项目,实现功能,深入最底层理解逻辑,学习成长。有时还会有意外收获,学到项目之外的知识;
    4.知道该功能有哪些坑,然后通过自己尝试或者观摩成熟解决方案来克服,避免以后遇到类似的坑;
    5.自己做出来后,能与同事或者小组甚至上传到gayhub和所有开发者共享,为社区做出自己的贡献,收获成就感。久而久之,就成为该细分领域的专家,所有人有问题都来找你,那是何等的荣誉!
    。。。


    位置参数科普

    各种内置位置参数

    想开发拖拽功能首先得理解各种位置参数,如图,大概分为这几类:

    event.clientX、event.clientY
    鼠标相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性

    event.pageX、event.pageY
    类似于event.clientX、event.clientY,但它们使用的是文档坐标而非窗口坐标。这2个属性不是标准属性,但得到了广泛支持。IE事件中没有这2个属性。

    event.offsetX、event.offsetY
    鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性。

    event.screenX、event.screenY
    鼠标相对于用户显示器屏幕左上角的X,Y坐标。标准事件和IE事件都定义了这2个属性

    clientWidth、clientHeight
    返回元素的宽高(包括元素宽高、内边距,不包括边框和外边距)

    offsetWidth、offsetHeight
    返回元素的宽高(包括元素宽高、内边距和边框,不包括外边距)

    功能介绍及思路

    功能页面

    基本功能很简单,就是点击弹框header部分按住进行拖动,弹框能跟着鼠标移动,松开则停止。左上角为鼠标坐标与弹框左上角坐标。
    细心的读者可以发现,两个坐标有一个差值。恭喜你发现了彩蛋,这个差值就是实现拖拽功能的核心!
    鼠标移动时我们能随时获取其位置,我们还能设置弹框左上角的位置,这两者是需要变化的值,同时,前者决定后者,后者跟随前者变化。
    那么,二者存在怎样的关系?我们需要一个不变的值,正如数学公式中的常量或系数一样。
    没错,就是前面提到的差值。我们发现,拖拽进行时,弹框左上角和鼠标的相对位置是不变的!
    顺藤摸瓜,也就是说,只要获取鼠标点击header时,其相对弹框左上角的坐标差,以此为常量,来控制弹框位置就可以了!
    另外还有一些优化,比如边界控制、位置修正等,详情见源码。

    代码实现

    Talk is cheap.Show me the code!

    // 获取鼠标点击title时的坐标、title的坐标以及两者的位移
      getPosition (e) {
        // 标题DOM元素titleDom
        const titleDom = e.target
        // titleDom的坐标(视窗)
        const X = titleDom.getBoundingClientRect().left
        // 由于Y轴出现滚动条,需要与鼠标保持一致,存储页面相对位置
        const Y = document.getElementsByClassName('group')[0].offsetTop
    
        // 鼠标点击的坐标(页面)
        let mouseX = e.pageX
        let mouseY = e.screenY
        // 鼠标点击位置与modal的位移
        const diffX = mouseX - X
        const diffY = mouseY - Y
        return {X, Y, mouseX, mouseY, diffX, diffY}
      }
     
      /**
       * 鼠标按下,设置modal状态为可移动,并注册鼠标移动事件
       * 计算鼠标按下时,指针所在位置与modal位置以及两者的差值
       **/
      onMouseDown (e) {
        const position = this.getPosition(e)
        window.onmousemove = this.onMouseMove
        window.onmouseup = this.onMouseUp
        this.setState({moving: true, diffX: position.diffX, diffY: position.diffY})
      }
     
      // 松开鼠标,设置modal状态为不可移动
      onMouseUp (e) {
        const { moving } = this.state
        moving && this.setState({moving: false});
      }
     
      // 鼠标移动重新设置modal的位置
      onMouseMove (e) {
        const {moving, diffX, diffY} = this.state
        if (moving) {
          // 获取鼠标位置数据
          const position = this.getPosition(e)
          // 计算modal应该随鼠标移动到的坐标
          const x = position.mouseX - diffX
          const y = position.mouseY - diffY
          // 窗口大小,结构限制,需要做调整,减去侧边栏宽度
          const { clientWidth, clientHeight } = document.documentElement
          const modal = document.getElementsByClassName("group")[0]
          if (modal) {
            // 计算modal坐标的最大值
            const maxHeight = clientHeight - modal.offsetHeight
            const maxWidth = clientWidth - modal.offsetWidth
            // 判断得出modal的最终位置,不得超出浏览器可见窗口
            const left = x > 0 ? (x < maxWidth ? x : maxWidth) : 0
            const top = y > 0 ? (y < maxHeight ? y : maxHeight) : 0
            this.setState({pageX: left, pageY: top})
          }
        }
      }
    

    原创不易,转载请注明出处。

    相关文章

      网友评论

          本文标题:react + 原生JS实现弹框拖拽解决方案

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