| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- import { nextTick, onBeforeUnmount, watch } from 'vue';
- import { applyWrapStyles, findModalWrap, queryModalWrap } from '../lib/modalWrap';
- /**
- * 弹框拖拽与居中:拖标题栏移动窗口,缩放环境下把鼠标视口坐标换算回布局坐标。
- *
- * 拖拽状态刻意用普通变量(非响应式),避免 mousemove 触发渲染;
- * modal-wrap 的定位样式统一走 lib/modalWrap,与面板侧的全屏逻辑共用同一套选择器。
- */
- export interface ChatDragDeps {
- getIsFullscreen: () => boolean;
- getVisible: () => boolean | undefined;
- }
- export const useChatDrag = (deps: ChatDragDeps) => {
- // 拖拽相关(使用普通变量避免 mousemove 触发 Vue 响应式导致卡顿)
- let isDragging = false;
- let dragStartX = 0;
- let dragStartY = 0;
- let modalStartLeft = 0;
- let modalStartTop = 0;
- // 容器 scale 缩放比(视觉尺寸/布局尺寸),用于把鼠标视口坐标换算回布局坐标
- let dragScaleX = 1;
- let dragScaleY = 1;
- let draggedEl: HTMLElement | null = null;
- const centerModal = () => {
- nextTick(() => {
- applyWrapStyles(deps.getIsFullscreen());
- draggedEl = null;
- });
- };
- const onDragStart = (e: MouseEvent) => {
- if (deps.getIsFullscreen()) return;
- draggedEl = findModalWrap(e.target as HTMLElement) || queryModalWrap();
- if (!draggedEl) return;
- // 记录容器缩放比,并把鼠标/弹框的视口坐标换算回布局坐标,避免不同分辨率下拖拽位移失真
- const rect = draggedEl.getBoundingClientRect();
- dragScaleX = draggedEl.offsetWidth ? rect.width / draggedEl.offsetWidth : 1;
- dragScaleY = draggedEl.offsetHeight ? rect.height / draggedEl.offsetHeight : 1;
- isDragging = true;
- dragStartX = e.clientX;
- dragStartY = e.clientY;
- modalStartLeft = rect.left / dragScaleX;
- modalStartTop = rect.top / dragScaleY;
- draggedEl.style.position = 'fixed';
- draggedEl.style.left = `${modalStartLeft}px`;
- draggedEl.style.top = `${modalStartTop}px`;
- draggedEl.style.margin = '0';
- draggedEl.style.transform = 'none';
- document.addEventListener('mousemove', onDragMove);
- document.addEventListener('mouseup', onDragEnd);
- };
- const onDragMove = (e: MouseEvent) => {
- if (!isDragging || !draggedEl) return;
- draggedEl.style.left = `${modalStartLeft + (e.clientX - dragStartX) / dragScaleX}px`;
- draggedEl.style.top = `${modalStartTop + (e.clientY - dragStartY) / dragScaleY}px`;
- };
- const onDragEnd = () => {
- isDragging = false;
- document.removeEventListener('mousemove', onDragMove);
- document.removeEventListener('mouseup', onDragEnd);
- };
- watch(deps.getVisible, (newVisible) => {
- if (newVisible) {
- centerModal();
- }
- });
- onBeforeUnmount(() => {
- document.removeEventListener('mousemove', onDragMove);
- document.removeEventListener('mouseup', onDragEnd);
- });
- return { onDragStart, centerModal };
- };
|