import { ref, onMounted, onUnmounted } from 'vue' import { getTouchDistance, getTouchCenter, getClientPosition } from '../utils/touchGestures' // 设计稿尺寸常量 const DESIGN_WIDTH = 1920 const DESIGN_HEIGHT = 1080 /** * 自由取景框模式的逻辑 * 支持拖动、四角调整大小、双指缩放 */ export function useViewfinderB() { // 取景框相关 const viewfinderContainer = ref() const viewfinderBox = ref() const viewfinder = ref({ x: 660, // (1920 - 600) / 2 = 660,居中 y: 340, // (1080 - 400) / 2 = 340,居中 width: 600, height: 400 }) // 拖动相关状态 const isDragging = ref(false) const dragStart = ref({ x: 0, y: 0 }) // 调整大小相关状态 const isResizing = ref(false) const resizeCorner = ref<'nw' | 'ne' | 'sw' | 'se' | null>(null) const resizeStart = ref({ x: 0, y: 0, boxX: 0, boxY: 0, boxWidth: 0, boxHeight: 0 }) // 双指缩放相关状态 const isPinching = ref(false) const pinchStart = ref({ distance: 0, centerX: 0, centerY: 0, boxWidth: 0, boxHeight: 0, boxX: 0, boxY: 0 }) // 将屏幕坐标转换为缩放后的本地坐标 const convertClientToLocal = (clientX: number, clientY: number) => { if (!viewfinderContainer.value) return { x: clientX, y: clientY } const containerRect = viewfinderContainer.value.getBoundingClientRect() // 计算缩放比例 const scaleX = containerRect.width / DESIGN_WIDTH const scaleY = containerRect.height / DESIGN_HEIGHT // 计算相对于容器的坐标,并转换为设计稿坐标系 const localX = (clientX - containerRect.left) / scaleX const localY = (clientY - containerRect.top) / scaleY return { x: localX, y: localY } } // 开始拖动取景框 const startDrag = (e: MouseEvent | TouchEvent) => { if (isResizing.value || isPinching.value) return // 检测双指触摸 if ('touches' in e && e.touches.length === 2) { // 双指缩放 isPinching.value = true const touch1 = e.touches[0] const touch2 = e.touches[1] pinchStart.value = { distance: getTouchDistance(touch1, touch2), centerX: (touch1.clientX + touch2.clientX) / 2, centerY: (touch1.clientY + touch2.clientY) / 2, boxWidth: viewfinder.value.width, boxHeight: viewfinder.value.height, boxX: viewfinder.value.x, boxY: viewfinder.value.y } e.preventDefault() return } // 单指拖动 if ('touches' in e && e.touches.length !== 1) return isDragging.value = true const { x: clientX, y: clientY } = getClientPosition(e) const localPos = convertClientToLocal(clientX, clientY) dragStart.value = { x: localPos.x - viewfinder.value.x, y: localPos.y - viewfinder.value.y } e.preventDefault() } // 开始调整大小 const startResize = (corner: 'nw' | 'ne' | 'sw' | 'se', e: MouseEvent | TouchEvent) => { isResizing.value = true resizeCorner.value = corner const { x: clientX, y: clientY } = getClientPosition(e) const localPos = convertClientToLocal(clientX, clientY) resizeStart.value = { x: localPos.x, y: localPos.y, boxX: viewfinder.value.x, boxY: viewfinder.value.y, boxWidth: viewfinder.value.width, boxHeight: viewfinder.value.height } e.preventDefault() } // 处理移动 const handleMove = (e: MouseEvent | TouchEvent) => { if (!isDragging.value && !isResizing.value && !isPinching.value) return // 处理双指缩放 if (isPinching.value && 'touches' in e && e.touches.length === 2) { const touch1 = e.touches[0] const touch2 = e.touches[1] const currentDistance = getTouchDistance(touch1, touch2) const scale = currentDistance / pinchStart.value.distance // 计算新的尺寸 let newWidth = pinchStart.value.boxWidth * scale let newHeight = pinchStart.value.boxHeight * scale const minSize = 200 // 限制最小和最大尺寸 newWidth = Math.max(minSize, Math.min(newWidth, DESIGN_WIDTH)) newHeight = Math.max(minSize, Math.min(newHeight, DESIGN_HEIGHT)) // 保持中心点不变,计算新的位置 const center = getTouchCenter(touch1, touch2) const localCenter = convertClientToLocal(center.x, center.y) let newX = localCenter.x - newWidth / 2 let newY = localCenter.y - newHeight / 2 // 边界检测 newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - newWidth)) newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - newHeight)) viewfinder.value.width = newWidth viewfinder.value.height = newHeight viewfinder.value.x = newX viewfinder.value.y = newY e.preventDefault() return } const { x: clientX, y: clientY } = getClientPosition(e) const localPos = convertClientToLocal(clientX, clientY) if (isDragging.value && viewfinderContainer.value) { // 拖动取景框 let newX = localPos.x - dragStart.value.x let newY = localPos.y - dragStart.value.y // 边界检测 - 使用设计稿尺寸 newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - viewfinder.value.width)) newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - viewfinder.value.height)) viewfinder.value.x = newX viewfinder.value.y = newY } else if (isResizing.value && viewfinderContainer.value) { // 调整取景框大小 const deltaX = localPos.x - resizeStart.value.x const deltaY = localPos.y - resizeStart.value.y const minSize = 200 // 最小尺寸 if (resizeCorner.value === 'se') { // 右下角 - 使用设计稿尺寸限制 viewfinder.value.width = Math.max( minSize, Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) ) viewfinder.value.height = Math.max( minSize, Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) ) } else if (resizeCorner.value === 'sw') { // 左下角 - 使用设计稿尺寸限制 const newWidth = resizeStart.value.boxWidth - deltaX const newX = resizeStart.value.boxX + deltaX if (newWidth >= minSize && newX >= 0) { viewfinder.value.x = newX viewfinder.value.width = newWidth } viewfinder.value.height = Math.max( minSize, Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) ) } else if (resizeCorner.value === 'ne') { // 右上角 - 使用设计稿尺寸限制 const newHeight = resizeStart.value.boxHeight - deltaY const newY = resizeStart.value.boxY + deltaY if (newHeight >= minSize && newY >= 0) { viewfinder.value.y = newY viewfinder.value.height = newHeight } viewfinder.value.width = Math.max( minSize, Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) ) } else if (resizeCorner.value === 'nw') { // 左上角 - 使用设计稿尺寸限制 const newWidth = resizeStart.value.boxWidth - deltaX const newHeight = resizeStart.value.boxHeight - deltaY const newX = resizeStart.value.boxX + deltaX const newY = resizeStart.value.boxY + deltaY if (newWidth >= minSize && newX >= 0) { viewfinder.value.x = newX viewfinder.value.width = newWidth } if (newHeight >= minSize && newY >= 0) { viewfinder.value.y = newY viewfinder.value.height = newHeight } } } e.preventDefault() } // 结束拖动或调整 const handleEnd = () => { isDragging.value = false isResizing.value = false isPinching.value = false resizeCorner.value = null } // 挂载时添加全局事件监听 onMounted(() => { document.addEventListener('mousemove', handleMove) document.addEventListener('mouseup', handleEnd) document.addEventListener('touchmove', handleMove) document.addEventListener('touchend', handleEnd) }) // 卸载时移除事件监听 onUnmounted(() => { document.removeEventListener('mousemove', handleMove) document.removeEventListener('mouseup', handleEnd) document.removeEventListener('touchmove', handleMove) document.removeEventListener('touchend', handleEnd) }) return { // Refs viewfinderContainer, viewfinderBox, // 状态 viewfinder, isDragging, isResizing, isPinching, // 方法 startDrag, startResize, // 常量 DESIGN_WIDTH, DESIGN_HEIGHT } }