编写可拖拽的弹窗

2018-12-07 admin

可拖拽的弹窗

在刚刚重构完的项目中使用到了element ui框架,踩了不少坑也学到了不少的东西。其中比较麻烦的是它的dialog弹窗组件是无法移动拖拽的,然而客户又强烈的要求一定要有这个功能,所以就自己写了个可拖拽的弹窗组件。虽然拖拽起来不是很流畅,但是也算是满足要求了。

1. 实现原理

主要的实现原理还是获取鼠标在div中的位置,获取位置后设置div的left和top来达到div跟随鼠标移动的效果。因为写的是vue,所以利用了vue的自定义指令来操作dom。

2. 实现步骤

2-1 设计盒子ui

  • 老实说,我经常被吐槽没有审美,设计的样式总是被喷。好在这次是dialog弹窗,网上有大把的参考样式。我大体参考了layer的弹窗做出了一个山寨弹窗。

    html代码

    <template>
     <div class="m_showBox" :class="skin">
       <div class="showBox_mask"></div>
       <div class="loading_wrap" v-if="buttonstatus === 1"></div>
       <div class="pop_box" id="pox-box" v-drag>
         <p class="pop_box_title">
           {{title || "提示"}}
           <span class="pop_box_close" @click="cancel"></span>
         </p>
         <div class="pop_box_content">
           <slot></slot>
         </div>
         <div class="pop_box_bottom">
           <a href="javascript:;"
              class="cancel_btn"
              @click="cancel">{{canceltext || "取消"}}</a>
           <a href="javascript:;"
              class="confirm_btn"
              v-if="type === 'confirm'"
              :class="{widths: buttonstatus === 1}"
              @click="confirm">
             <svg viewBox="25 25 50 50" class="u-circular" v-if="buttonstatus === 1">
               <circle cx="50" cy="50" r="20" fill="none" class="path"></circle>
             </svg>
             <span :class="{'marginLeft': buttonstatus === 1}">{{confirmtext || '确定'}}</span>
           </a>
         </div>
       </div>
     </div>
    </template>
    

    css代码太长放到github上了vueDrag.vue

    效果图:

    图片描述

  • 设计要点: 1、背景遮罩 我这里选择了使用了3个遮罩板,第一块是覆盖全屏幕的白色遮罩(m_showBox)使用fixed定位,让弹窗的所有内容与浏览器之间不会出现留白。第2块就是上图看到的灰色背景(showBox_mask),用来突显弹窗。最后一块是点击确定的遮罩窗(loading_wrap),来防止提交ajax时,用户点击按钮或修改弹窗数据。 2、弹窗构成 这里的弹窗就包括标题,内容和底部部分。内容部分通过插槽插入内容,底部按钮通过svg来实现提交加载的loading效果。

2-2 定义组件props

属性 描述
skin 用于控制弹窗的宽度
title 弹窗标题(small, middle, large)
type 弹窗类型
confirmtext 确认键文案
canceltext 取消键文案
buttonstatus 控制按钮加载效果

通过传入的props值来设置弹窗的样式和文案。

2-3 自定义事件实现按钮回调

  • confirm和cancel自定义事件 定义自定义按钮事件,使用$emit触发。

    methods: {
        cancel: function () {
          this.$emit("cancel");
        },
        confirm: function () {
          if (this.buttonstatus === 1) {
            return;
          }
          this.$emit("confirm");
        },
      },
    

2-4 自定义指令drag实现拖拽效果

2-4-1 vue的directives。

通过vue自定义指令获取绑定的元素,在对DOM进行操作。关于更多vue自定义指令用法,移步自定义指令

2-4-2 相关属性(事件对象event,dom元素,window对象)。

  1. event.clientX:clientX事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。
  2. event.clientY:clientY事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的垂直坐标。
  3. offsetLeft/offsetLeftTop属性:可以返回当前元素距离某个父辈元素左边与顶部的距离(虽然我的父级遮罩层有了定位,但是它的宽高都是与body保持一致的)。
  4. offsetWidth/offsetHeight: 返回任何一个元素宽/高度,包括边框和填充
  5. window.innerHeight/Width: 获取当前页面可视区的宽高(包括滚动条)。

2-4-3 相关事件

事件 描述
onmousedown 鼠标按钮被按下。
onmousemove 鼠标被移动。
onmouseup/td> 鼠标按键被松开。

2-4-4 实现代码

directives: {
      drag: {
        inserted: function (el, binding, vnode) {
          vnode = vnode.elm;
          el.onmousedown = ((event) => {
            if (event.target.className !== "pop_box_title") {
              return;
            }
            //获取鼠标在盒子中的位置
            let mouseX = event.clientX - vnode.offsetLeft;
            let mouseY = event.clientY - vnode.offsetTop;
            //绑定移动和停止函数
            document.onmousemove = ((event) => {
              let left, top;
              //获取新的鼠标位置对应下的盒子应该在的位置
              left = event.clientX - mouseX;
              top = event.clientY - mouseY;
              //获取div在页面中X轴的最小最大位置
              let minX = vnode.offsetWidth / 2;
              let maxX = (window.innerWidth - vnode.offsetWidth / 2) - 10//去掉滚动条的宽度
              if (left <= minX) {
                left = minX;
              } else if (left >= maxX) {
                left = maxX;
              }
              //获取div在页面中Y轴的最大最小位置
              let minY = vnode.offsetHeight / 2;
              let maxY = (window.innerHeight - vnode.offsetHeight / 2);
              if (top <= minY) {
                top = minY;
              } else if (top >= maxY) {
                top = maxY;
              }
              //赋值移动
              vnode.style.left = left + 'px';
              vnode.style.top = top + 'px';
           });
           document.onmouseup = (() => {
             document.onmousemove = document.onmouseup = null;
           });
         });
         window.onresize = (() =>{
            vnode.style.left = "50%";
            vnode.style.top = "50%";
         });
     }
   }
}

2-4-5 代码解析

1、给弹窗绑定onmousedown事件,获取到鼠标在弹窗中的位置(以弹窗左上角为原点)。 2、document绑定onmousemove事件,获取当前的鼠标位置,当前鼠标位置减去鼠标在弹窗的相当位置即可得到此时弹窗应该处于的位置。然后在通过style设置弹窗的位置。 3、鼠标松开解绑document的鼠标事件。

注意点:

  1. 弹窗要一直在页面可视区移动,最大的移动距离就是可视区的宽高减去盒子本身的宽高(还要考虑到浏览器的滚动条的宽高,我的浏览器滚动条是自己设置的,高度为0,宽度为10)。 window.innerHeight - vnode.offsetHeight / 2; (window.innerWidth - vnode.offsetWidth / 2) - 10;
  2. 只有弹窗标题才能拖拽,所以判断非标题部分之间return。
  3. 浏览器窗口大小改变会影响弹窗的位置,监听改变浏览器窗口改变把弹窗居中。

2-5 使用

  1. 下载drag.vue。vueDrag.vue

  2. 使用import引入

    import vDrag from "./dragDiv.vue"
    
  3. 控制弹窗的显示隐藏通过v-if绑定data里的数据即可。

    <transition name="el-fade-in">
        <v-drag v-if="isShow" :tilte="title" :type="type" @confirm="confirmSubmit" @cancel="cancel" :buttonstatus="buttonstatus">
          <el-form label-width="100px">
            <el-form-item label="用户名称:">
              <el-input placeholder="请输入用户名" v-model="username"></el-input>
            </el-form-item>
            <el-form-item label="密码:">
              <el-input placeholder="请输入密码" v-model="password"></el-input>
            </el-form-item>
          </el-form>
        </v-drag>
      </transition>
    

    图片描述

结语

关于这个组件我觉得还有很多优化的地方,望各位大佬给出意见。

原文链接:https://segmentfault.com/a/1190000017300987

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-47564.html

文章标题:编写可拖拽的弹窗

相关文章
vue+element-ui+slot-scope实现可编辑表格
1.咱开发拿到需求大多数是去网上找成型的组件,找不到再看原生的方法能否实现,大牛除外哈,大牛一般喜欢封装组件框架。 2.可编辑表格在后台管理系统还是比较常用的,因为比较流行框架element,iview都没有这个应用,所以考虑了两种方法,下...
2017-12-25
css布局的各种FC简单介绍:BFC,IFC,GFC,FFC
什么是FC? Formatting Context,格式化上下文,指页面中一个渲染区域,拥有一套渲染规则,它决定了其子元素如何定位,以及与其他元素的相互关系和作用。 BFC 什么是BFC Block Formatting Context,块...
2018-05-17
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰�杞�闆呴粦, Arial, sans-serif &quot;&gt;2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿,对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长,DCloud CEO王安也发表了文章,从开发者和用户两个角度分析了HTML对两个人群的优势。其实,关于HTML5的开发工具,我们以往的...
2015-11-12
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
HTML5凭什么可以代替Flash
本世纪初,全球网络建设仍处于早期阶段,发达国家网民刚刚在从窄带向宽带网络过渡。由于网络带宽、PC运算速度等因素限制,早期的网站基本以静态文字和图片内容为主。但随着宽带网络在全球范围快速普及,网民对内容的需求也不断变化。死板的文字加图片的网站...
2015-11-12
AJAX的浏览器支持
AJAX 的要点是 XMLHttpRequest 对象。 不同的浏览器创建 XMLHttpRequest 对象的方法是有差异的。 IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 Jav...
2015-11-12
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
回到顶部