Vue:可拖拽组件

        在实际开发中,很可能会遇到开发可拖拽组件的需求,目的是应对某些弹框组件会遮盖某些重要信息/可操作面板,通过可拖拽的形式可以将上层的弹框组件移动到其他位置,从而不影响整个系统的操作。下面,我们分两步走,开发一个可拖拽的弹框组件,最终效果如下图所示,

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">&#xe678;</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>

猜你喜欢

转载自blog.csdn.net/weixin_43524214/article/details/129104717