模态框
模态对话框(Modal Dialogue Box,又叫做模式对话框),是指在用户想要对对话框以外的应用程序进行操作时,必须首先对该对话框进行响应
摘自百度百科
先放上效果图:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNiZpdmLwcDNyETNyIDMwMjNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.gif)
基本功能为点击窗口中间的发光按钮,使模态框出现,此时除了模态框部分,页面其他部分无法再点击,必须先点击确定对该模态框进行响应后才能进行其他操作。鼠标按住并拖动模态框标题处,可以实现模态框的拖拽跟随效果
实现
模态框效果的实现分为屏幕中间按钮和模态框确定按钮的点击事件的实现,以及模态框拖拽效果的实现。
由于两个按钮的点击事件只需要令模态框和一个模糊遮罩的显示或消失即可,实现比较简单,不做赘述,重点分析一下这个拖拽效果怎么实现
拖拽效果涉及到的鼠标事件有
mouseup
、
mousedown
和
mousemove
,还需要通过鼠标事件对象的
pageX
和
pageY
属性和元素的
offsetLeft
和
offsetTop
属性来实时计算模态框的位置(模态框为固定定位,通过
top
和
left
实现框体移动),以此实现模态框的拖拽效果
可拖拽区域
首先要明确的是,有效的拖拽区域应该设置为模态框的一部分区域(这里设置为标题区域),这是因为网页的模态框往往有多个功能部分(如若干个输入框、按钮等),如果模态框整个部分都可以拖拽,那么就有可能影响功能区域交互效果
拖拽点
所谓的拖拽点就是当鼠标在模态框可拖拽区域按下时的那个点。需要计算出这个点相对于模态框左上角的坐标,这个坐标将用于后续模态框随鼠标光标移动时边移量的计算
当鼠标按下时,触发
mousedown
事件,利用鼠标事件对象的
pageX
和
pageY
属性标识出当前光标位置,再利用模态框的偏移量属性
offsetLeft
和
offsetTop
计算出拖拽点坐标,计算公式为:
x = e.pageX - offsetLeft;
y = e.pageY - offsetTop;
模态框位置计算
当鼠标在模态框可拖拽区域按下并移动时,模态框要随着鼠标移动,这个移动是通过改变模态框的上边移量
top
和左边移量
left
实现的(模态框为固定定位)。
top
和
left
的值随鼠标坐标位置的改变而改变,利用上述求出的拖拽点相对于模态框左上角的坐标
(x, y)
以及相关推导关系,可以得出:
left = e.pageX - x;
top = e.pageY - y;
事件监听器的设置
至此,就可以将上述计算关系应用到
mousedown
和
mousemove
事件中进行拖拽效果的实现了
首先拖拽效果的触发,发生在鼠标在模态框可拖拽区域按下时,因此应该给模态框可拖拽区域的元素添加
mousedown
监听器(这里可拖拽区域元素为
modalTitle
):
modalTitle.onmousedown = function(e) {
// 计算拖拽点位置
let x = e.pageX - modalTitle.offsetLeft;
let y = e.pageY - modalTitle.offsetTop;
// ......
}
要明确的一点是,只有在鼠标在模态框可拖拽区域按下时才需要鼠标移动事件
mousemove
发生(鼠标移动触发时可以不停的更新
e.pageX
和
e.pageY
的值以便实时计算出模态框边移量),如果鼠标移动事件一直存在,那么有可能在鼠标压根没有点击拖拽时模态框就乱动了。所以
mousemove
应该在鼠标按下时绑定事件,在鼠标松开(
mouseuup
)时解绑事件:
modalTitle.onmousedown = function(e) {
// 计算拖拽点位置
let x = e.pageX - modalTitle.offsetLeft;
let y = e.pageY - modalTitle.offsetTop;
// 鼠标按下时绑定事件
targetElem.onmousemove = function(e) {
modalCon.style.left = e.pageX - x;
modalCon.style.top = e.pageY - y;
}
}
modalTitle.onmouseup = function(e) {
// 鼠标松开时解绑事件
targetElem.onmousemove = ''
}
// 上述的 targetElem 时 mousemove 事件监听器应用的元素
那么
mousemove
的监听器应该应用在哪个元素上呢?既然是在模态框的可拖拽区域按下鼠标的,那就应用到模态框的可拖拽区域?但是如果应用到模态框的可拖拽区域(
modalTitle
)会导致一种结果就是:当鼠标移动太快,鼠标光标有可能会脱离模态框,导致失败的拖拽效果
那么应该应用在哪里?可以看到,鼠标是在整个文档的可视范围内移动的,那么
mousemove
监听器直接应用到
document
即可
modalTitle.addEventListener('mousedown', (e) => {
let x = e.pageX - modalCon.offsetLeft;
let y = e.pageY - modalCon.offsetTop;
document.onmousemove = (e) => {
modalCon.style.left = e.pageX - x + 'px';
modalCon.style.top = e.pageY - y + 'px';
}
})
modalTitle.addEventListener('mouseup', (e) => {
document.onmousemove = '';
})
至此整个拖拽效果就完成了!!!
演示链接
代码的CodePen演示链接
源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模态框</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #1e1e1e;
user-select: none;
}
#show-modal {
font-family: serif;
width: 200px;
height: 50px;
background: none;
border: 4px solid;
border-color: #73c991;
font-size: 20px;
font-weight: bold;
color: #73c991;
box-shadow: 0px 0px 4px 0px;
animation: blur-effect 4s ease-in-out 0s infinite;
}
#show-modal:hover {
animation-play-state: paused;
box-shadow: 0px 0px 15px 4px !important;
}
@keyframes blur-effect {
0% {
box-shadow: 0px 0px 4px 0px;
}
25% {
box-shadow: 0px 0px 15px 4px;
}
50% {
box-shadow: 0px 0px 4px 0px;
}
75% {
box-shadow: 0px 0px 15px 4px;
}
100% {
box-shadow: 0px 0px 4px 0px;
}
}
#modal-con {
display: none;
position: fixed;
z-index: 999;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#modal-box {
width: 400px;
height: 200px;
background-color: #333333;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
border-radius: 20px;
color: #6eaadc;
user-select: none;
}
#modal-title {
width: 100%;
text-align: center;
font-size: 30px;
cursor: move;
}
#modal-content {
font-size: 18px;
}
#modal-btn {
background: none;
border: 1px solid;
color: #6eaadc;
width: 50px;
}
#mask {
position: fixed;
top: 0px;
left: 0px;
z-index: 1;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: none;
}
</style>
</head>
<body>
<button id="show-modal">显示模态框</button>
<div id="mask"></div>
<div id="modal-con">
<div id="modal-box">
<h3 id="modal-title">模态框</h3>
<p id="modal-content">我是模态框</p>
<button id="modal-btn">确定</button>
</div>
</div>
<script>
let modalCon = document.getElementById('modal-con');
let modalTitle = document.getElementById('modal-title');
let modalBtn = document.getElementById('modal-btn');
let mask = document.getElementById('mask');
let showBtn = document.getElementById('show-modal');
// 显示模态框和遮罩层
showBtn.addEventListener('click', () => {
modalCon.style.display = 'block';
mask.style.display = 'block';
})
// 隐藏模态框和遮罩层
modalBtn.addEventListener('click', (e) => {
modalCon.style.display = 'none';
mask.style.display = 'none';
e.stopPropagation()
})
// 关键:实现模态框拖动效果
// 注意应当只能靠模态框部分区域拖动模态框,功能部分(如按钮)不应该作为可拖动区域
modalTitle.addEventListener('mousedown', (e) => {
let x = e.pageX - modalCon.offsetLeft;
let y = e.pageY - modalCon.offsetTop;
document.onmousemove = (e) => {
modalCon.style.left = e.pageX - x + 'px';
modalCon.style.top = e.pageY - y + 'px';
}
})
modalTitle.addEventListener('mouseup', (e) => {
document.onmousemove = '';
})
</script>
</body>
</html>