当前位置: 首页 > news >正文

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

vue cli上简单的功能,在js上太难弄了,这个弹窗功能时常用到,保存起来备用吧
备注:deepseek这个人工智障写一堆有问题的我,还老服务器繁忙

效果图:

在这里插入图片描述

html代码:

<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代码:

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代码:

/* 遮罩层样式 */
  .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;
        }
      }
    }
  }
http://www.dtcms.com/a/107994.html

相关文章:

  • SQL语句(一)—— DDL
  • [Lc6_记忆化搜索] 最长递增子序列 | 矩阵中的最长递增路径
  • 【大模型系列篇】大模型基建工程:使用 FastAPI 构建 SSE MCP 服务器
  • 14-SpringBoot3入门-MyBatis-Plus之CRUD
  • 树莓派超全系列文档--(15)无需交互使用raspi-config工具其二
  • clickhouse集群版本部署文档
  • jenkins 参数化发布到服务器 publish over ssh、label、Parameterized publishing
  • 基于DeepSeek、ChatGPT支持下的地质灾害风险评估、易发性分析、信息化建库及灾后重建
  • js实现一个可指定超时时间的异步函数重试机制
  • tomcat 目录结构组成
  • python的def
  • 计算机网络 第二章:应用层(1)
  • 结构体补充:位段
  • 我该怎么设置SVN客户端的认证信息?
  • C++ I/O 流通俗指南
  • 【卫星参数】高分二号卫星参数光谱波段_应用说明_数据来源
  • 查询条件与查询数据的ajax拼装
  • MySQL-- 函数(单行函数):数值函数, 字符串函数
  • STM32单片机入门学习——第8节: [3-4] 按键控制LED光敏传感器控制蜂鸣器
  • 深度剖析:U盘打不开难题与应对之策
  • Github 2025-03-30 php开源项目日报 Top10
  • 鸿蒙学习笔记(4)-Radio组件、弹框组件、组件内部状态、工具类
  • python数据结构——链表、栈、队列
  • 安徽京准:NTP时间同步服务器操作使用说明
  • 从数据透视到AI分析,用四层架构解决运维难题
  • 图解AUTOSAR_SWS_CANTransceiverDriver
  • Nginx基本配置文件详解
  • 自然语言处理(23:(第六章3.)​seq2seq模型的改进)
  • UG NX二次开发(C++)-采用Open/C与NXOpen获取曲线的长度
  • 网络安全的现状与防护措施