// 弹窗拖动 import { ref, Ref, watch, nextTick, computed, reactive } from 'vue'; type posType = { top: number; left: number; }; type directionType = | 'TOP' | 'RIGHT' | 'BOTTOM' | 'LEFT' | 'TOP_RIGHT' | 'BOTTOM_RIGHT' | 'BOTTOM_LEFT' | 'TOP_LEFT'; type baseSizeType = { /** * 允许拖动方向 上/上右/右/下右/下/下左/左/上左 */ resizeDirection: boolean[]; layoutTopHeight: number; windowHeight: number; windowWidth: number; // 窗口模式的尺寸 winWidth: number; winHeight: number; winMinWidth: number; minWidth: number; minHeight: number; maxHeight: number; maxWidth: number; transformX: number; transformY: number; defaultWidth: number; defaultHeight: number; width: number; height: number; }; type initSizeType = { /** 默认宽 */ width?: number; /** 默认高 */ height?: number; /** 最小宽 */ minWidth?: number; /** 最小高 */ minHeight?: number; /** 允许拖动方向 上/上右/右/下右/下/下左/左/上左 */ resizeDirection?: boolean[]; /** 初始定位 */ defaultPosition?: string; }; /*** * 初始化默认弹窗位置 */ const initPos = { right: 14, top: 60 }; const getSizeToUnit = (num: number, unit = 'px') => { return num > 0 ? num + unit : num + ''; }; /** * @params classList 可拖动地方的class值,也为唯一值 * @params boxClass 容器class值必须为唯一值 * @params dragShow 弹窗是否显示 * @params initSize 默认尺寸 */ export default function useDrag( classList: string[], boxClass: string, dragShow: Ref, initSize?: initSizeType ) { const windowInfo = reactive({ // 小窗口 侧边大窗口 currentType: 'SMALL' as 'SMALL' | 'LARGE', // 弹窗,还是还原 windowType: 'SMALL' as 'SMALL' | 'LARGE', // showScreen: false, // 是否全屏显示 showType: 'MENU' as 'MENU' | 'CONTENT' // 当前显示哪一部分 - 如果是全屏显示则无效 }); const pos = ref({ top: -1, // -1 为初始值 代表没有缓存 默认居中 left: -1 }); watch( () => windowInfo.windowType, () => { if (windowInfo.windowType === 'LARGE') { baseSize.resizeDirection = [ true, true, true, true, true, true, true, true ]; } else if (windowInfo.windowType === 'SMALL') { baseSize.resizeDirection = [ true, false, false, false, true, false, false, false ]; } const dragDirectionPoints = document.querySelectorAll( `.${boxClass} .dragDirectionPoint` ); dragDirectionPoints.forEach((element: any, index) => { if (baseSize.resizeDirection[index]) { element.style.pointerEvents = 'all'; } else { element.style.pointerEvents = 'none'; } }); } ); const styleDrag = computed(() => { return { ...dragStyles, width: getSizeToUnit(baseSize.width), height: getSizeToUnit(baseSize.height), transform: `translate(${baseSize.transformX}px, ${baseSize.transformY}px)` }; }); const baseSize = reactive({ resizeDirection: initSize?.resizeDirection || [ true, false, false, false, true, false, false, false ], layoutTopHeight: 0, windowHeight: window.innerHeight, windowWidth: window.innerWidth, // 窗口模式的尺寸 winWidth: 1010, winHeight: 650, winMinWidth: initSize?.minWidth || 800, minWidth: initSize?.minWidth || 400, minHeight: initSize?.minHeight || 340, maxHeight: window.innerHeight, maxWidth: window.innerWidth > 1024 ? 1024 : window.innerWidth, transformX: window.innerWidth - 400 - initPos.right, transformY: (window.innerHeight - 640) / 2, defaultWidth: initSize?.width || 400, defaultHeight: initSize?.height || 640, height: initSize?.height || 640, width: initSize?.width || 400 }); const dragStyles = reactive({ maxHeight: getSizeToUnit(baseSize.maxHeight), minWidth: getSizeToUnit(baseSize.minWidth), minHeight: getSizeToUnit(baseSize.minHeight) }); nextTick(() => { const layoutTopHeight = document.querySelector('.layoutTop')?.clientHeight || 0; baseSize.layoutTopHeight = Math.ceil(layoutTopHeight); baseSize.windowHeight = window.innerHeight - layoutTopHeight; baseSize.maxHeight = window.innerHeight - layoutTopHeight; // 判断窗口的高度与默认高度比例 if (baseSize.defaultHeight >= baseSize.maxHeight) { baseSize.defaultHeight = baseSize.maxHeight - 100; } const translateY = (baseSize.windowHeight - baseSize.defaultHeight) / 2; baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth - initPos.right; baseSize.transformY = translateY; dragStyles.maxHeight = getSizeToUnit(baseSize.maxHeight); // 初始化定位 if (initSize?.defaultPosition === 'center') { // alert(initSize.width) const transformX = (window.innerWidth - baseSize.defaultWidth) / 2; const transformY = (baseSize.windowHeight - baseSize.defaultHeight) / 2; baseSize.transformX = transformX; baseSize.transformY = transformY; } const boxClassDom = document.querySelector(`.${boxClass}`) as HTMLElement; if (!boxClassDom) { return; } addReSizeDom(boxClassDom, baseSize.resizeDirection); classList.map((className: string) => { const classDom = document.querySelector(`.${className}`) as HTMLElement; if (classDom) { classDom.style.cursor = 'move'; drag(classDom, boxClassDom, baseSize); } }); }); // watch(dragShow, () => { // if (dragShow.value) { // // 初始化pos值 // // initPos(); // window.addEventListener('resize', refreshPos); // // nextTick(() => { // // const boxClassDom = document.querySelector( // // `.${boxClass}` // // ) as HTMLElement; // // if (!boxClassDom) { // // return; // // } // // console.log(boxClassDom, 'boxClassDom'); // // classList.map((className: string) => { // // const classDom = document.querySelector( // // `.${className}` // // ) as HTMLElement; // // if (classDom) { // // classDom.style.cursor = 'move'; // // drag(classDom, boxClassDom, baseSize); // // } // // }); // // }); // } else { // window.removeEventListener('resize', refreshPos); // } // }); /** * 添加功能放大缩小操作DOM * @param parentElement {添加拖动父级元素} * @param direction {允许拖动的位置 上/上右/右/下右/下/下左/左/上左} */ function addReSizeDom(parentElement: HTMLElement, direction: boolean[] = []) { function addResizeDirection(params: { width: string; height: string; direction: directionType; top?: string | any; right?: string | any; bottom?: string | any; left?: string | any; cursor: string; zIndex?: string; pointerEvents: string; }) { const dom = document.createElement('div'); dom.className = 'dragDirectionPoint'; dom.style.position = 'absolute'; dom.style.userSelect = 'none'; dom.style.width = params.width; dom.style.height = params.height; dom.style.left = params.left; dom.style.top = params.top; dom.style.bottom = params.bottom; dom.style.right = params.right; dom.style.zIndex = params.zIndex || '9'; dom.style.cursor = params.cursor; dom.style.pointerEvents = params.pointerEvents; parentElement.appendChild(dom); drag(dom, parentElement, baseSize, 'RESIZE', params.direction); } // 上 addResizeDirection({ width: '100%', height: '10px', left: '0', top: '-5px', cursor: 'row-resize', direction: 'TOP', pointerEvents: direction[0] ? 'all' : 'none' }); // 上右 addResizeDirection({ width: '20px', height: '20px', right: '-10px', top: '-10px', zIndex: '10', cursor: 'ne-resize', direction: 'TOP_RIGHT', pointerEvents: direction[1] ? 'all' : 'none' }); // 右 addResizeDirection({ width: '10px', height: '100%', top: '0', right: '-5px', cursor: 'col-resize', direction: 'RIGHT', pointerEvents: direction[2] ? 'all' : 'none' }); // 下右 addResizeDirection({ width: '20px', height: '20px', right: '-10px', bottom: '-10px', cursor: 'se-resize', zIndex: '10', direction: 'BOTTOM_RIGHT', pointerEvents: direction[3] ? 'all' : 'none' }); // 下 addResizeDirection({ width: '100%', height: '10px', left: '0', bottom: '-5px', cursor: 'row-resize', direction: 'BOTTOM', pointerEvents: direction[4] ? 'all' : 'none' }); // 下左 addResizeDirection({ width: '20px', height: '20px', left: '-10px', bottom: '-10px', cursor: 'sw-resize', zIndex: '10', direction: 'BOTTOM_LEFT', pointerEvents: direction[5] ? 'all' : 'none' }); // 左 addResizeDirection({ width: '10px', height: '100%', top: '0', left: '-5px', cursor: 'col-resize', direction: 'LEFT', pointerEvents: direction[6] ? 'all' : 'none' }); // 上左 addResizeDirection({ width: '20px', height: '20px', left: '-10px', top: '-10px', cursor: 'nw-resize', zIndex: '10', direction: 'TOP_LEFT', pointerEvents: direction[7] ? 'all' : 'none' }); } function refreshPos() { // if (pos.value.left === -1 && pos.value.top === -1) { // return; // } // const boxClassDom = document.querySelector(`.${boxClass}`) as HTMLElement; // if (!boxClassDom) return; // const parentElementRect = boxClassDom.getBoundingClientRect(); // const clientWidth = document.documentElement.clientWidth; // const clientHeight = document.documentElement.clientHeight; // const { top, left } = pos.value; // const maxLeft = clientWidth - parentElementRect.width; // const maxTop = clientHeight - parentElementRect.height; // let moveX = left; // let moveY = top; // const minLeft = 0; // const minTop = 0; // moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX; // moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY; // pos.value = { // top: moveY, // left: moveX // }; onReset(); } /** 切换窗口 */ function onScreen() { if (windowInfo.windowType === 'SMALL') { windowInfo.windowType = 'LARGE'; baseSize.transformX = (baseSize.windowWidth - baseSize.winWidth) / 2; baseSize.transformY = (baseSize.windowHeight - baseSize.winHeight) / 2 - baseSize.layoutTopHeight / 2; baseSize.width = baseSize.winWidth; baseSize.height = baseSize.winHeight; } else if (windowInfo.windowType === 'LARGE') { windowInfo.windowType = 'SMALL'; const translateY = (baseSize.windowHeight - baseSize.defaultHeight) / 2; baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth - initPos.right; baseSize.transformY = translateY > initPos.top ? translateY + (translateY - initPos.top) : translateY; baseSize.width = baseSize.defaultWidth; baseSize.height = baseSize.defaultHeight; } } /** 格式化尺寸 */ function onResize() { windowInfo.windowType = 'SMALL'; if (windowInfo.currentType === 'SMALL') { windowInfo.currentType = 'LARGE'; baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth; baseSize.transformY = 0; baseSize.width = baseSize.defaultWidth; baseSize.height = baseSize.maxHeight; } else if (windowInfo.currentType === 'LARGE') { windowInfo.currentType = 'SMALL'; baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth - initPos.right; baseSize.transformY = baseSize.windowHeight - baseSize.defaultHeight - initPos.top; baseSize.width = baseSize.defaultWidth; baseSize.height = baseSize.defaultHeight; } } /** 重置样式 */ function onReset() { windowInfo.currentType = 'SMALL'; windowInfo.windowType = 'SMALL'; if (initSize?.defaultPosition === 'center') { const transformX = (window.innerWidth - baseSize.defaultWidth) / 2; const transformY = (baseSize.windowHeight - baseSize.defaultHeight) / 2; baseSize.transformX = transformX; baseSize.transformY = transformY; } else { baseSize.transformX = baseSize.windowWidth - baseSize.defaultWidth - initPos.right; baseSize.transformY = baseSize.windowHeight - baseSize.defaultHeight - initPos.top; } baseSize.width = baseSize.defaultWidth; baseSize.height = baseSize.defaultHeight; } return { pos, baseSize, windowInfo, styleDrag, onScreen, onResize, onReset }; } // 拖动 function drag( el: HTMLElement, parentElement: HTMLElement, baseSize: baseSizeType, type = 'MOVE' as 'MOVE' | 'RESIZE', direction?: directionType ) { function onDown(e: MouseEvent | TouchEvent) { const isTouchEv = isTouchEvent(e); const event = isTouchEv ? e.touches[0] : e; const parentElementRect = parentElement.getBoundingClientRect(); const downX = event.clientX; const downY = event.clientY; const clientWidth = document.documentElement.clientWidth; const clientHeight = document.documentElement.clientHeight; const maxLeft = clientWidth - parentElementRect.width; const maxTop = clientHeight - parentElementRect.height - baseSize.layoutTopHeight; const maxResizeLeft = clientWidth - baseSize.winMinWidth - (clientWidth - parentElementRect.right); const maxResizeTop = clientHeight - baseSize.minHeight - baseSize.layoutTopHeight; const minLeft = 0; const minTop = 0; const baseHeight = JSON.parse(JSON.stringify(baseSize.height)); const baseWidth = JSON.parse(JSON.stringify(baseSize.width)); function onTop(moveY: number) { const maxSuffix = parentElementRect.bottom - baseSize.minHeight - baseSize.layoutTopHeight; moveY = moveY > maxSuffix ? maxSuffix : moveY; const suffix = baseSize.transformY - moveY; if (suffix > 0 || baseSize.height > baseSize.minHeight) { baseSize.transformY = moveY; baseSize.height = baseSize.height + suffix; } } function onRight(moveX: number) { const suffix = Math.ceil( baseWidth + moveX - (baseSize.width + baseSize.transformX) ); if (suffix > 0 || baseSize.width > baseSize.winMinWidth) { baseSize.width = baseSize.width + suffix >= baseSize.maxWidth ? baseSize.maxWidth : baseSize.width + suffix; } } function onBottom(moveY: number) { if (baseSize.maxHeight > baseSize.height) { const suffix = Math.ceil( baseHeight + moveY - (baseSize.height + baseSize.transformY) ); baseSize.height = baseSize.height + suffix; } } function onLeft(moveX: number) { moveX = moveX < minLeft ? minLeft : moveX > maxResizeLeft ? maxResizeLeft : moveX; const suffix = baseSize.transformX - moveX; if (suffix > 0 || baseSize.width > baseSize.winMinWidth) { if (baseSize.width + suffix <= baseSize.maxWidth) { baseSize.transformX = moveX; baseSize.width = baseSize.width + suffix; } } } function onMove(e: MouseEvent | TouchEvent) { const event = isTouchEvent(e) ? e.touches[0] : e; if (type === 'MOVE') { let moveX = parentElementRect.left + (event.clientX - downX); let moveY = parentElementRect.top - baseSize.layoutTopHeight + (event.clientY - downY); moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX; moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY; // 移动 baseSize.transformY = moveY; baseSize.transformX = moveX; } else if (type === 'RESIZE') { let moveY = parentElementRect.top - baseSize.layoutTopHeight + (event.clientY - downY); moveY = moveY < minTop ? minTop : moveY > maxResizeTop ? maxResizeTop : moveY; const moveX = parentElementRect.left + (event.clientX - downX); // 拖动 if (direction === 'TOP') { onTop(moveY); } else if (direction === 'RIGHT') { onRight(moveX); } else if (direction === 'BOTTOM') { onBottom(moveY); } else if (direction === 'LEFT') { onLeft(moveX); } else if (direction === 'TOP_RIGHT') { onTop(moveY); onRight(moveX); } else if (direction === 'BOTTOM_RIGHT') { onBottom(moveY); onRight(moveX); } else if (direction === 'BOTTOM_LEFT') { onBottom(moveY); onLeft(moveX); } else if (direction === 'TOP_LEFT') { onTop(moveY); onLeft(moveX); } } } function onUp() { document.removeEventListener( isTouchEv ? 'touchmove' : 'mousemove', onMove ); document.removeEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp); } document.addEventListener(isTouchEv ? 'touchmove' : 'mousemove', onMove); document.addEventListener(isTouchEv ? 'touchend' : 'mouseup', onUp); } el.addEventListener('mousedown', onDown); el.addEventListener('touchstart', onDown); } function isTouchEvent(e: MouseEvent | TouchEvent): e is TouchEvent { return window.TouchEvent && e instanceof window.TouchEvent; }