150 lines
3.8 KiB
TypeScript
150 lines
3.8 KiB
TypeScript
import { ref } from 'vue'
|
|
import { getTouchDistance, getClientPosition } from '../utils/touchGestures'
|
|
|
|
/**
|
|
* 手机拍照模式的取景逻辑
|
|
* 支持缩放和拖拽功能
|
|
*/
|
|
export function useViewfinderA() {
|
|
// 缩放限制
|
|
const MIN_ZOOM = 1
|
|
const MAX_ZOOM = 3
|
|
|
|
// 缩放级别 (保留用于点击按钮)
|
|
const zoomLevels = [3, 2, 1]
|
|
const currentZoom = ref(1)
|
|
const isCapturing = ref(false) // 拍照闪光效果
|
|
|
|
// 拖拽相关状态
|
|
const isDragging = ref(false)
|
|
const dragStartX = ref(0)
|
|
const dragStartY = ref(0)
|
|
const offsetX = ref(0)
|
|
const offsetY = ref(0)
|
|
const currentOffsetX = ref(0)
|
|
const currentOffsetY = ref(0)
|
|
|
|
// 双指缩放相关
|
|
const isPinching = ref(false)
|
|
const pinchStartDistance = ref(0)
|
|
const pinchStartZoom = ref(1)
|
|
|
|
// 计算边界限制
|
|
const limitOffset = () => {
|
|
const containerWidth = 871
|
|
const containerHeight = 420
|
|
const scale = currentZoom.value
|
|
|
|
// 计算图片缩放后的尺寸
|
|
const scaledWidth = containerWidth * scale
|
|
const scaledHeight = containerHeight * scale
|
|
|
|
// 计算最大偏移量(图片边缘不能超出容器中心)
|
|
const maxOffsetX = (scaledWidth - containerWidth) / 2
|
|
const maxOffsetY = (scaledHeight - containerHeight) / 2
|
|
|
|
// 限制偏移量
|
|
currentOffsetX.value = Math.max(-maxOffsetX, Math.min(maxOffsetX, currentOffsetX.value))
|
|
currentOffsetY.value = Math.max(-maxOffsetY, Math.min(maxOffsetY, currentOffsetY.value))
|
|
}
|
|
|
|
// 设置缩放级别
|
|
const setZoom = (zoom: number) => {
|
|
currentZoom.value = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom))
|
|
limitOffset()
|
|
}
|
|
|
|
// 鼠标滚轮缩放
|
|
const handleWheel = (e: WheelEvent) => {
|
|
e.preventDefault()
|
|
// 滚轮向下是正数(缩小),向上是负数(放大)
|
|
const delta = -e.deltaY * 0.001
|
|
const newZoom = currentZoom.value + delta
|
|
setZoom(newZoom)
|
|
}
|
|
|
|
// 开始拖拽 / 触摸
|
|
const startDrag = (e: MouseEvent | TouchEvent) => {
|
|
// 双指缩放
|
|
if ('touches' in e && e.touches.length === 2) {
|
|
isPinching.value = true
|
|
isDragging.value = false
|
|
pinchStartDistance.value = getTouchDistance(e.touches[0], e.touches[1])
|
|
pinchStartZoom.value = currentZoom.value
|
|
e.preventDefault()
|
|
return
|
|
}
|
|
|
|
// 单指拖拽
|
|
if ('touches' in e && e.touches.length !== 1) return
|
|
|
|
isDragging.value = true
|
|
isPinching.value = false
|
|
|
|
const { x: clientX, y: clientY } = getClientPosition(e)
|
|
|
|
dragStartX.value = clientX
|
|
dragStartY.value = clientY
|
|
offsetX.value = currentOffsetX.value
|
|
offsetY.value = currentOffsetY.value
|
|
}
|
|
|
|
// 拖拽中 / 缩放中
|
|
const onDrag = (e: MouseEvent | TouchEvent) => {
|
|
// 处理双指缩放
|
|
if (isPinching.value && 'touches' in e && e.touches.length === 2) {
|
|
const currentDistance = getTouchDistance(e.touches[0], e.touches[1])
|
|
const scale = currentDistance / pinchStartDistance.value
|
|
setZoom(pinchStartZoom.value * scale)
|
|
e.preventDefault()
|
|
return
|
|
}
|
|
|
|
if (!isDragging.value) return
|
|
|
|
const { x: clientX, y: clientY } = getClientPosition(e)
|
|
|
|
const deltaX = clientX - dragStartX.value
|
|
const deltaY = clientY - dragStartY.value
|
|
|
|
currentOffsetX.value = offsetX.value + deltaX
|
|
currentOffsetY.value = offsetY.value + deltaY
|
|
|
|
limitOffset()
|
|
|
|
// 防止滚动
|
|
if (e.cancelable) {
|
|
e.preventDefault()
|
|
}
|
|
}
|
|
|
|
// 结束拖拽
|
|
const endDrag = () => {
|
|
isDragging.value = false
|
|
isPinching.value = false
|
|
}
|
|
|
|
// 计算transform样式
|
|
const getTransform = () => {
|
|
return `translate(${currentOffsetX.value}px, ${currentOffsetY.value}px) scale(${currentZoom.value})`
|
|
}
|
|
|
|
return {
|
|
// 状态
|
|
zoomLevels,
|
|
currentZoom,
|
|
isCapturing,
|
|
isDragging,
|
|
currentOffsetX,
|
|
currentOffsetY,
|
|
|
|
// 方法
|
|
setZoom,
|
|
startDrag,
|
|
onDrag,
|
|
endDrag,
|
|
handleWheel,
|
|
getTransform
|
|
}
|
|
}
|