封装可拖动弹窗(vue jquery引入到html的版本)

vue cli上简单的功能,在js上太难弄了,这个弹窗功能时常用到,保存起来备用吧

备注:deepseek这个人工智障写一堆有问题的我,还老服务器繁忙

效果图:

html代码:

javascript 复制代码
<div class="modal-mask" v-show="qrcodeShow" @click.self="closeModal">
  <div class="modal-container" ref="modal" :style="modalStyle">
    <div
      class="modal-header"
      @mousedown="startDrag"
      @touchstart.prevent="startDrag"
      @mouseup="stopDrag"
      @touchend="stopDrag"
    >
      <span>获取app</span>
      <span class="close-btn" @click="closeModal">&times;</span>
    </div>
    <div class="image-container">
      <img :src="qrcodeImgUrl" class="modal-image" alt="弹窗图片" />
    </div>
  </div>
</div>

js代码:

javascript 复制代码
data: {
	scanCodeList: [],
    qrcodeShow: false,
    qrcodeImgUrl: "**图片地址**",
    isDragging: false,
    startX: 0,
    startY: 0,
    translateX: 0,
    translateY: 0,
    modalRect: null,
},
  created() {
    this.$nextTick(() => {
      // 使用jQuery添加动画效果
      $(".modal-container").hide();
      // 监听弹窗状态变化
      this.$watch("qrcodeShow", (newVal) => {
        if (newVal) {
          $(".modal-container").fadeIn(300);
        } else {
          $(".modal-container").fadeOut(300);
        }
      });
    });
  },
  computed: {
    modalStyle() {
      return {
        transform: `translate(${this.translateX}px, ${this.translateY}px)`,
      };
    },
  },
  methods: {
    showModal() {
      this.qrcodeShow = true;
    },
    closeModal() {
      this.qrcodeShow = false;
    },
    // 开始拖动
    startDrag(e) {
      this.isDragging = true;
      const clientX = e.touches ? e.touches[0].clientX : e.clientX;
      const clientY = e.touches ? e.touches[0].clientY : e.clientY;

      // 记录初始位置
      this.startX = clientX - this.translateX;
      this.startY = clientY - this.translateY;

      // 获取弹窗尺寸
      this.modalRect = this.$refs.modal.getBoundingClientRect();

      // 添加事件监听
      document.addEventListener("mousemove", this.onDrag);
      document.addEventListener("touchmove", this.onDrag, { passive: false });
      document.addEventListener("mouseup", this.stopDrag);
      document.addEventListener("touchend", this.stopDrag);

      // 优化拖动体验
      document.body.style.cursor = "grabbing";
      document.body.style.userSelect = "none";
    },

    // 拖动处理
    onDrag(e) {
      if (!this.isDragging) return;

      // 获取坐标
      const clientX = e.touches ? e.touches[0].clientX : e.clientX;
      const clientY = e.touches ? e.touches[0].clientY : e.clientY;

      // 计算新位置
      let newX = clientX - this.startX;
      let newY = clientY - this.startY;

      // 计算边界
      const viewportWidth = document.documentElement.clientWidth;
      const viewportHeight = document.documentElement.clientHeight;
      const modalWidth = this.modalRect.width;
      const modalHeight = this.modalRect.height;

      // 有效边界
      const minX = -(viewportWidth - modalWidth) / 2;
      const minY = -(viewportHeight - modalHeight) / 2;
      const maxX = (viewportWidth - modalWidth) / 2;
      const maxY = (viewportHeight - modalHeight) / 2;

      // 应用约束
      newX = Math.max(minX, Math.min(newX, maxX));
      newY = Math.max(minY, Math.min(newY, maxY));

      // 更新位置
      this.translateX = newX;
      this.translateY = newY;
    },

    // 停止拖动
    stopDrag() {
      this.isDragging = false;

      // 移除事件监听
      document.removeEventListener("mousemove", this.onDrag);
      document.removeEventListener("touchmove", this.onDrag);
      document.removeEventListener("mouseup", this.stopDrag);
      document.removeEventListener("touchend", this.stopDrag);

      // 恢复样式
      document.body.style.cursor = "";
      document.body.style.userSelect = "";
    },

    // 重置位置到屏幕中央
    resetPosition() {
      this.$nextTick(() => {
        const modal = this.$refs.modal;
        if (modal) {
          const rect = modal.getBoundingClientRect();
          const viewportWidth = document.documentElement.clientWidth;
          const viewportHeight = document.documentElement.clientHeight;

          this.translateX = (viewportWidth - rect.width) / 2;
          this.translateY = (viewportHeight - rect.height) / 2;
        }
      });
    },
  },

css代码:

css 复制代码
/* 遮罩层样式 */
  .modal-mask {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    z-index: 9998;
    display: flex;
    justify-content: center;
    align-items: center;

    /* 弹窗容器 */
    .modal-container {
      display: none;
      background: white;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
      width: 500px;
      height: 500px;
      z-index: 9999;
      position: relative;

      /* 弹窗头部 */
      .modal-header {
        height: 50px;
        padding: 15px;
        border-bottom: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        align-items: center;
        cursor: move;
        user-select: none; /* 防止文字被选中 */
        span {
          font-size: 18px;
          font-weight: bold;
        }
        /* 关闭按钮样式 */
        .close-btn {
          cursor: pointer;
          font-size: 20px;
          color: #666;
          padding: 0 5px;
        }
      }

      /* 图片容器 */
      .image-container {
        padding: 20px;
        width: 100%;
        height: calc(100% - 50px);
        overflow: auto;
        img {
          width: 100%;
          height: 100%;
          object-fit: cover;
        }
      }
    }
  }
相关推荐
追风筝的人er10 小时前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
前端·vue.js·后端
编程老船长13 小时前
解决不同项目需要不同 Node.js 版本的问题
前端·vue.js
xiaogg367815 小时前
spring oauth2 单点登录
java·vue.js·spring
前端那点事15 小时前
Vue前端SEO优化全攻略(实操落地版,新手也能上手)
前端·vue.js
Dxy123931021615 小时前
HTML 如何使用 SVG 画曲线
前端·算法·html
棉猴15 小时前
Python海龟绘图之绘制文本
javascript·python·html·write·turtle·海龟绘图·输出文本
计算机学姐16 小时前
基于微信小程序的校园失物招领管理系统【uniapp+springboot+vue】
java·vue.js·spring boot·mysql·信息可视化·微信小程序·uni-app
fix一个write十个17 小时前
从零搭建音视频通话太痛苦?这个 Vue3 CallKit 让你 5 分钟搞定 1v1 + 群聊通话
前端·vue.js·github
小歪 | 前端17 小时前
VUE_运行Vue项目Network: unavailable问题解决
前端·javascript·vue.js
Cyan_RA917 小时前
SpringMVC REST 详解
java·spring·mvc·springmvc·restful·jquery·jsp