在实际开发中,很可能会遇到开发可拖拽组件的需求,目的是应对某些弹框组件会遮盖某些重要信息/可操作面板,通过可拖拽的形式可以将上层的弹框组件移动到其他位置,从而不影响整个系统的操作。下面,我们分两步走,开发一个可拖拽的弹框组件,最终效果如下图所示,
Step-1:原生JS开发可拖拽弹框
第一步,基于原生JS开发一个简陋版本的可拖拽弹框,基本效果贴在本部分最后,先看一下完整的示例代码吧,
<!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>*,html,body {padding: 0;margin: 0;box-sizing: border-box;}body {background-color: #000;}.box {position: absolute;top: 15px;left: 200px;width: 400px;height: 300px;background-color: rgba(255, 255, 255, 0.3);border: 1px solid #fff;}</style>
</head><body><div class="box" id="box"></div>
</body>
<script>let boxPos = {x: null,y: null}let startPos = {x: null,y: null}let isMove = false;const box = document.getElementById("box");box.addEventListener("mousedown", (e) => {const { clientX, clientY } = e;//获取元素的当前位置boxPos = {x: box.offsetLeft,y: box.offsetTop,};startPos = {x: clientX,y: clientY}isMove = true;document.body.style.cursor = "move";document.addEventListener("mousemove", (e) => {if (!isMove) return;const { clientX, clientY } = e;const [offsetX, offsetY] = [clientX - startPos.x, clientY - startPos.y];box.style.left = `${boxPos.x + offsetX}px`box.style.top = `${boxPos.y + offsetY}px`})document.addEventListener("mouseup", () => {isMove = false;document.body.style.cursor = "default";})})</script></html>
基本效果如下,PS:直接使用了黑色背景,弹框也没有做美化,因为我们的重点工作在后面的组件式开发。
Step-2:Vue可拖拽弹框组件
完整的示例代码如下,直接引入InfoBox.vue组件即可使用,
<template><div v-if="show" ref="infoBox" class="info-box" :style="styleObject"><div class="nav" @mousedown.stop="mouseDownHandler"><span class="title">{{ title }}</span><span class="iconfont close" @click="show = false"></span></div><div class="body"><slot name="content"></slot></div></div>
</template>
<script>
export default {name: "InfoBox",props: {width: {type: String,required: false,default: "400px",},height: {type: String,required: false,default: "300px",},title: {type: String,required: false,default: '点位详情'}},data() {return {styleObject: {width: "400px",height: "300px"},show: true,isMove:false,}},mounted() {this.styleObject = {height: this.$props.height,width: this.$props.width,}},methods: {mouseDownHandler(e) {const currentPosition = {x:this.$refs.infoBox.offsetLeft,y:this.$refs.infoBox.offsetTop};const startPosition = {x:e.clientX,y:e.clientY};//获取当前点击位置console.log(currentPosition,startPosition);this.isMove = true;//注册鼠标移动事件document.addEventListener("mousemove",(event_move)=>{if(!this.isMove) return;const offsetX = event_move.clientX - startPosition.x,offsetY = event_move.clientY - startPosition.y;console.log(offsetX,offsetY)//修改弹框位置this.$refs.infoBox.style.left = `${currentPosition.x + offsetX}px`;this.$refs.infoBox.style.top = `${currentPosition.y + offsetY}px`;})//注册鼠标抬起事件document.addEventListener("mouseup",()=>{this.isMove = false;})},mousemoveHandler() {},mouseUpHandler() {}}
}
</script>
<style lang="less" scoped>
.info-box {// .box-size(400px,300px);position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);background-color: @base-box-bgColor;border: 1px solid #3374ac;z-index: 999999;.nav {padding: 5px 10px;height: 35px;background: @base-bg-color;.flex-layout('row');justify-content: space-between;.title {user-select: none; //禁止选中标题文字padding: 0px 5px;font-family: "AliMaMa";font-style: italic;font-size: 18px;color: #cee9f5;background-image: linear-gradient(to top, #00C6FF, #8AFFD2);/* 线性渐变背景,方向向上 */-webkit-background-clip: text;/* 背景被裁剪成文字的前景色 */-webkit-text-fill-color: transparent;/* 文字填充颜色变透明 */}.close {font-size: 20px;cursor: pointer;&:hover {color: rgb(0, 201, 252);}}}
}
</style>
使用示例如下,
<!--* @Description: * @Author: Xwd* @Date: 2023-02-15 22:26:06* @LastEditors: Xwd* @LastEditTime: 2023-02-18 21:08:26
-->
<template><div id="app"><InfoBox/><router-view/></div>
</template>
<script>
//导入可拖拽弹框组件
import InfoBox from '@/layout/common/InfoBox.vue'
export default {name:"App",components:{InfoBox}
}
</script>
<style lang="less">
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;.box-size(100vw,100vh);
}
</style>