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

前言

项目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})
      }
    }
  }

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,470评论 1 11
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,411评论 1 45
  • 事件是什么,可以用来做什么,什么时候用到它? 事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。JavaScr...
    茂茂爱吃鱼阅读 1,506评论 0 16
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,351评论 0 17
  • JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬...
    LemonnYan阅读 673评论 0 4