编写可拖拽的弹窗

可拖拽的弹窗

在刚刚重构完的项目中使用到了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

    效果图:

    ![](https://img2018.cnblogs.com/blog/1504647/201812/1504647-20181214210254482-1736032359.png)

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

2-2 定义组件props

属性 描述
skin 用于控制弹窗的宽度(small, middle, large)
title 弹窗标题
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) =&gt; {
            if (event.target.className !== "pop_box_title") {
              return;
            }
            //获取鼠标在盒子中的位置
            let mouseX = event.clientX - vnode.offsetLeft;
            let mouseY = event.clientY - vnode.offsetTop;
            //绑定移动和停止函数
            document.onmousemove = ((event) =&gt; {
              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 &lt;= minX) {
                left = minX;
              } else if (left &gt;= maxX) {
                left = maxX;
              }
              //获取div在页面中Y轴的最大最小位置
              let minY = vnode.offsetHeight / 2;
              let maxY = (window.innerHeight - vnode.offsetHeight / 2);
              if (top &lt;= minY) {
                top = minY;
              } else if (top &gt;= maxY) {
                top = maxY;
              }
              //赋值移动
              vnode.style.left = left + 'px';
              vnode.style.top = top + 'px';
           });
           document.onmouseup = (() =&gt; {
             document.onmousemove = document.onmouseup = null;
           });
         });
         window.onresize = (() =&gt;{
            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://img2018.cnblogs.com/blog/1504647/201812/1504647-20181214210304223-1376696165.png)

结语

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

原文地址:https://segmentfault.com/a/1190000017300044

原文地址:https://www.cnblogs.com/datiangou/p/10121682.html

时间: 2025-01-15 00:56:04

编写可拖拽的弹窗的相关文章

jQuery的DOM操作实例(2)——拖拽效果&amp;&amp;拓展插件

一.原生JavaScript编写拖拽效果 二.jQuery编写的拖拽效果 三.在jQuery中拓展一个拖拽插件

js实现模态窗口的拖拽功能

一.用到的几个基本概念: (1)document.body.clientWidth/clientHeight :获得BODY对象宽度/高度              (2)document.documentElement.clientWidth/clientHeight:用来获得可见区域宽度/高度. clientWidth/clientHeight的计算方法: (3)offsetParent 是一个只读属性,返回一个指向最近的(closest,指包含层级上的最近)包含该元素的定位元素.如果没有定

原生弹窗拖拽代码demo+简单的抽奖

拖拽效果 效果: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>弹窗拖拽</title> <style> *{margin:0;padding:0;} .box{position: absolute;width: 400px;height: 300px;top:100px;left:

弹窗拖拽组件开发应用

需要注意的问题包括: 1.this的指向到底是指向谁--弄清楚所指的对象 2.深入理解原型的概念及使用: 去改写对象下面公用的方法或者属性 , 让公用的方法或者属性在内存中存在一份 ( 提高性能) 1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 5 <

简单弹窗拖拽

<!doctype html><!--<!DOCTYPE> 声明位于文档中的最前面的位置,处于 <html> 标签之前.此标签可告知浏览器文档使用哪种 HTML 或 XHTML 规范.--><html>    <head>        <!--声明当前页面的编码集:charset=gbk,gb2312(中文编码),utf-8国际编码-->        <meta http-equiv="Content-

Java实现HTML5拖拽文件上传

这是主页面 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+reques

android开发游记:SpringView 下拉刷新的高效解决方案,定制你自己风格的拖拽页面

关于下拉刷新/上拉加载更多的解决方案网上已经有很多了,浏览了目前主流的下拉控件比如PullToRefresh库等,第一:大多数实现库都难以进行动画和样式的自定义.第二:不能很好的兼容多种滚动控件,它们都对listView.RecyclerView等进行了不同程度的重新实现,你在项目中就得使用库提供的PullToRefreshListView.PullToRefreshRecyclerView等来代替源生的listView.RecyclerView等,这样的方式其实并不好,随着android版本的

Kean专题:拖动一个属性块(JIG拖拽)

原文转载自:http://through-the-interface.typepad.com/through_the_interface/jigs/(该口已无法访问) 可访问转载入口:http://bbs.mjtd.com/thread-75618-1-1.html(转载自明镜通道by雪山飞狐_lzh) 原kean博客已经无法看到,故转载明经通道 雪山飞狐_lzh 老师整理内容     1.kean原文翻译     March 18, 2009 Jigging an AutoCAD block

HTML5 拖拽实现

简介: 最早在网页中引入JavaScript拖放功能是IE4.当时,网页中只有两种对象可以拖放:图像和某些文本.拖放图像时,把鼠标放到图像上,按住鼠标不放就可以拖放它.拖放文本时,要先选中文本,然后可以像拖放图像一样拖放被选中的文本.在IE4中,唯一有效的放置目标是文本框.到了IE5,拖放功能得到拓展,添加了新的事件,而且几乎网页中的任何元素都可以作为放置目标.IE5.5更进一步让网页中的任何元素都可以拖放.HTML5以IE的实例为基础指定了拖放规范.  一.基本释义  1 实现拖拽效果   *