antd vue+tsx+vueuse 实现可拖拽并且右边可以拉伸的模态框

其中拉伸指令可以用我https://www.cnblogs.com/llcdbk/p/18648406 这篇文章提到的自定义指令,

拖拽功能主要参考 antd中关于模态框拖拽的代码,但是我这里做了一些改进,我要求模态框只能在我的页面窗口内拖拽,向下和向右向左的时候要做边界判断,并且,窗口大小重新调整的时候,模态框的位置还原为初始化,具体代码如下:

复制代码
import {
  computed,
  defineComponent,
  onBeforeUnmount,
  onMounted,
  ref,
  watch,
  watchEffect,
} from 'vue'
// 样式引入
import { Button, Input, Modal } from 'ant-design-vue'
import './index.less' // 引入样式文件
import { useDraggable } from '@vueuse/core' // 引入拖拽功能

// 定义组件
export default defineComponent({
  name: 'MyModal', // 组件名称
  setup() {
    //#region 弹窗拖拽功能
    const modalTitleRef: any = ref() // 模态框标题的引用,用于拖拽

    // 使用vueuse的拖拽功能,获取当前拖拽位置和状态
    const { x, y, isDragging } = useDraggable(modalTitleRef)

    const startX = ref<number>(0) // 拖拽开始时的X坐标
    const startY = ref<number>(0) // 拖拽开始时的Y坐标
    const startedDrag = ref(false) // 拖拽状态标识
    const transformX = ref(0) // 当前X轴变换值
    const transformY = ref(0) // 当前Y轴变换值
    const preTransformX = ref(0) // 拖拽前的X轴变换值
    const description = ref('') // 模态框描述内容
    const preTransformY = ref(0) // 拖拽前的Y轴变换值
    const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 }) // 拖拽区域的边界
    const rightBoundary = 30 // 右侧边界
    const bottomBoundary = 20 // 底部边界

    // 监听拖拽位置变化
    watch([x, y], () => {
      if (!startedDrag.value) {
        startX.value = x.value // 记录拖拽开始时的X坐标
        startY.value = y.value // 记录拖拽开始时的Y坐标
        const bodyRect = document.body.getBoundingClientRect() // 获取文档的边界矩形
        const titleRect = modalTitleRef.value.getBoundingClientRect() // 获取标题的边界矩形
        const modalRect = document
          .getElementById('modal-render')
          .getBoundingClientRect() // 获取模态框的边界矩形

        // 计算拖拽区域的右边界和底边界
        dragRect.value.right = bodyRect.width - titleRect.width - rightBoundary
        dragRect.value.bottom =
          bodyRect.height - modalRect.height + bottomBoundary
        preTransformX.value = transformX.value // 记录当前的X轴变换值
        preTransformY.value = transformY.value // 记录当前的Y轴变换值
      }
      startedDrag.value = true // 设置拖拽状态为已开始
    })

    // 监听拖拽状态变化
    watch(isDragging, () => {
      if (!isDragging) {
        startedDrag.value = false // 如果不再拖拽,重置拖拽状态
      }
    })

    // 监听拖拽效果
    watchEffect(() => {
      if (startedDrag.value) {
        // 计算新的transformX
        transformX.value =
          preTransformX.value +
          Math.min(
            Math.max(dragRect.value.left, x.value), // 限制X轴的最小值和最大值
            dragRect.value.right
          ) -
          startX.value // 减去拖拽开始时的X坐标

        // 计算新的transformY
        transformY.value =
          preTransformY.value +
          Math.min(
            Math.max(dragRect.value.top, y.value), // 限制Y轴的最小值和最大值
            dragRect.value.bottom
          ) -
          startY.value // 减去拖拽开始时的Y坐标
      }
    })

    // 计算模态框的变换样式
    const transformStyle: any = computed(() => {
      return {
        transform: `translate(${transformX.value}px, ${transformY.value}px)`, // 设置变换样式
        position: 'relative', // 设置相对定位
        cursor: isDragging.value ? 'grabbing' : 'grab', // 根据拖拽状态设置光标样式
      }
    })
    //#endregion

    // 更新拖拽区域的边界
    const updateDragRect = () => {
      const bodyRect = document.body.getBoundingClientRect() // 获取文档的边界矩形
      const modalRect = document
        .getElementById('modal-render')
        .getBoundingClientRect() // 获取模态框的边界矩形

      startedDrag.value = false // 重置拖拽状态
      if (modalTitleRef.value) {
        const ballRect = modalTitleRef.value.getBoundingClientRect() // 获取标题的边界矩形
        dragRect.value.right = bodyRect.width - ballRect.width // 更新右边界
        dragRect.value.bottom = bodyRect.height - ballRect.height // 更新底边界

        // 计算左侧边界
        const leftBoundary = 0 // 左侧边界为0

        // 如果transformX小于左侧边界,调整transformX
        if (transformX.value < leftBoundary) {
          transformX.value = leftBoundary // 设置为左侧边界
        }

        // 如果transformX小于左侧边界减去自身宽度,调整transformX
        if (transformX.value < leftBoundary - modalRect.width) {
          transformX.value = leftBoundary - modalRect.width // 确保模态框完全可见
        }
      }
    }

    // 组件挂载时添加事件监听
    onMounted(() => {
      window.addEventListener('resize', updateDragRect) // 监听窗口大小变化
    })

    // 组件卸载时移除事件监听
    onBeforeUnmount(() => {
      window.removeEventListener('resize', updateDragRect) // 移除窗口大小变化监听
    })

    // 渲染模态框
    return () => (
      <>
        <Modal
          open={true} // 模态框是否打开
          mask={false} // 是否显示遮罩
          width={500} // 模态框宽度
          destroyOnClose={true} // 关闭时销毁模态框
          maskClosable={false} // 点击遮罩是否关闭模态框
          wrapClassName="config-panel" // 模态框外层类名
          closable={false} // 是否显示关闭按钮
          bodyStyle={{ height: 'calc(100vh - 250px)', overflow: 'auto' }} // 模态框内容样式
          v-slots={{
            title: () => (
              <div
                class="flex items-center justify-between" // 标题样式
                ref={modalTitleRef} // 标题引用
                style="width: 100%; cursor: move" // 设置宽度和光标样式
              >
                我的测试模态框
              </div>
            ),
            modalRender: ({ originVNode }) => {
              return (
                <div
                  id="modal-render" // 模态框渲染区域ID
                  class="modal-render" // 模态框渲染区域类名
                  style={{ ...transformStyle.value }} // 应用变换样式
                  v-el-drawer-drag-width // 自定义指令
                >
                  {originVNode}
                </div>
              )
            },
            footer: () => (
              <div class="flex justify-end">
                <Button size="middle" style="margin-right: 8px">
                  取消
                </Button>
                <Button size="middle" type="primary">
                  提交
                </Button>
              </div>
            ),
          }}
        >
          <div class="action-description">
            <Input v-model={description.value} placeholder="添加描述" />
          </div>
        </Modal>
      </>
    )
  },
})
复制代码

效果如下

 

posted @   洛晨随风  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2020-01-03 docker 加速镜像的地址收集
2020-01-03 mongodb 的ID转换实体要注意的地方
2018-01-03 大数据量高并发的数据库优化详解(MSSQL)
点击右上角即可分享
微信分享提示