forked from zhongyidian/super_video
feat: 对接各个视频数据
This commit is contained in:
1 parent
6103453e32
commit
6ab98c4764
339 files changed
+17316
-1531
No files matched your search
@@ -0,0 +1,287 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { getBackgroundImage, getStaticImage } from '@/utils'
|
||||
import { ImgButton } from '@/components/Button'
|
||||
import { Overlay } from '@/components/Overlay'
|
||||
import { useViewfinderB } from './useViewfinderB'
|
||||
import { useCanvasCapture } from '../composables/useCanvasCapture'
|
||||
import StartOverlay from '../components/StartOverlay.vue'
|
||||
import LoadingOverlay from '@/components/Loading/LoadingOverlay.vue'
|
||||
import { GuideSteps, GuideStepsItem } from '@/components/Guide'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useFilmStore } from '@/stores'
|
||||
|
||||
const router = useRouter()
|
||||
const start = ref(false)
|
||||
const showInstruction = ref<boolean>(false)
|
||||
|
||||
type ResizeDirection = 'nw' | 'ne' | 'sw' | 'se'
|
||||
|
||||
// 基础逻辑
|
||||
const back = (): void => router.back()
|
||||
|
||||
const hideInstruction = (): void => {
|
||||
showInstruction.value = false
|
||||
}
|
||||
|
||||
const handleStart = (): void => {
|
||||
start.value = true
|
||||
showInstruction.value = true
|
||||
}
|
||||
|
||||
// 截图功能
|
||||
const { isLoading, showModal, capturedImageUrl, capture } = useCanvasCapture()
|
||||
|
||||
const handleModalClose = () => {
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
// 自由取景框模式
|
||||
const { viewfinderContainer, viewfinderBox, viewfinder, startDrag, startResize } = useViewfinderB()
|
||||
|
||||
const handleStartDrag = (event: MouseEvent | TouchEvent): void => {
|
||||
hideInstruction()
|
||||
startDrag(event)
|
||||
}
|
||||
|
||||
const handleStartResize = (direction: ResizeDirection, event: MouseEvent | TouchEvent): void => {
|
||||
hideInstruction()
|
||||
startResize(direction, event)
|
||||
}
|
||||
|
||||
const showGuide = ref(false)
|
||||
const backgroundImage = ref<HTMLImageElement | null>(null)
|
||||
|
||||
// Canvas 截图配置常量
|
||||
const UI_UPDATE_DELAY = 50 // UI 更新延迟(ms)
|
||||
const CANVAS_SCALE = 2 // Canvas 缩放比例(2倍高清)
|
||||
const GUIDE_SHOW_DELAY = 1000 // 引导显示延迟(ms)
|
||||
|
||||
/**
|
||||
* 截取场景
|
||||
* 使用 Canvas 手动绘制,确保移动端兼容性
|
||||
*/
|
||||
/**
|
||||
* 截取场景
|
||||
* 使用 Canvas 手动绘制,确保移动端兼容性
|
||||
*/
|
||||
const captureScene = async (): Promise<void> => {
|
||||
if (isLoading.value) return
|
||||
|
||||
hideInstruction()
|
||||
|
||||
await capture({
|
||||
width: viewfinder.value.width,
|
||||
height: viewfinder.value.height,
|
||||
scale: CANVAS_SCALE,
|
||||
delay: UI_UPDATE_DELAY,
|
||||
onDraw: ctx => {
|
||||
if (!backgroundImage.value) {
|
||||
throw new Error('Background image not found')
|
||||
}
|
||||
|
||||
// 计算图片实际尺寸和设计稿尺寸的缩放比例
|
||||
// viewfinder 的坐标是基于设计稿尺寸(1920x1080)
|
||||
// 但 backgroundImage 的实际尺寸可能不同
|
||||
const imageScaleX = backgroundImage.value.naturalWidth / 1920
|
||||
const imageScaleY = backgroundImage.value.naturalHeight / 1080
|
||||
|
||||
// 绘制背景图片的裁剪区域
|
||||
// 将设计稿坐标转换为图片实际坐标
|
||||
ctx.drawImage(
|
||||
backgroundImage.value,
|
||||
viewfinder.value.x * imageScaleX, // 源图 x(转换为实际坐标)
|
||||
viewfinder.value.y * imageScaleY, // 源图 y(转换为实际坐标)
|
||||
viewfinder.value.width * imageScaleX, // 源图 width(转换为实际坐标)
|
||||
viewfinder.value.height * imageScaleY, // 源图 height(转换为实际坐标)
|
||||
0, // 目标 x
|
||||
0, // 目标 y
|
||||
viewfinder.value.width, // 目标 width
|
||||
viewfinder.value.height // 目标 height
|
||||
)
|
||||
},
|
||||
onComplete: () => {
|
||||
// 延迟显示引导
|
||||
setTimeout(() => {
|
||||
showGuide.value = true
|
||||
}, GUIDE_SHOW_DELAY)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成引导,跳转到 Vlog 页面
|
||||
*/
|
||||
const handleComplete = () => {
|
||||
showGuide.value = false
|
||||
useFilmStore().setFilmUnlockProgress(3)
|
||||
router.push({
|
||||
name: 'Film'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full h-full">
|
||||
<div
|
||||
class="relative w-full h-full bg-cover bg-center bg-no-repeat shooting-bg"
|
||||
:style="{
|
||||
backgroundImage: getBackgroundImage('film/shooting/油罐艺术公园模糊底图.png')
|
||||
}"
|
||||
>
|
||||
<!-- 标题 -->
|
||||
<ImgButton image="shooting/标题-取景地b.png" class="title-button absolute left-0" />
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<ImgButton
|
||||
image="shooting/返回.png"
|
||||
class="back-button absolute right-0 z-50"
|
||||
@click="back"
|
||||
/>
|
||||
|
||||
<p
|
||||
v-if="start && showInstruction"
|
||||
class="absolute left-1/2 -translate-x-1/2 top-[220px] z-30 text-red-500 text-4xl leading-normal font-bold"
|
||||
>
|
||||
操作说明:拖动取景框四角调整裁剪范围,点击“取景”键就完成啦!
|
||||
</p>
|
||||
|
||||
<StartOverlay scene="b" v-if="!start" @start="handleStart" />
|
||||
|
||||
<!-- 取景 -->
|
||||
<div class="absolute bottom-0 w-full h-full" v-else>
|
||||
<!-- 隐藏的背景图片,用于 Canvas 绘制 -->
|
||||
<img
|
||||
ref="backgroundImage"
|
||||
class="absolute inset-0 w-full h-full object-cover pointer-events-none opacity-0"
|
||||
:src="getStaticImage('film/shooting/龙美术馆清晰图.png')"
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<!-- 取景框容器 -->
|
||||
<div ref="viewfinderContainer" class="relative w-full h-full">
|
||||
<!-- 四个方向的遮罩层 -->
|
||||
<!-- 上遮罩 -->
|
||||
<div
|
||||
class="mask-layer absolute left-0 right-0 bg-black/60 pointer-events-none z-10"
|
||||
:style="{
|
||||
top: 0,
|
||||
height: viewfinder.y + 'px'
|
||||
}"
|
||||
></div>
|
||||
<!-- 下遮罩 -->
|
||||
<div
|
||||
class="mask-layer absolute left-0 right-0 bg-black/60 pointer-events-none z-10"
|
||||
:style="{
|
||||
top: viewfinder.y + viewfinder.height + 'px',
|
||||
bottom: 0
|
||||
}"
|
||||
></div>
|
||||
<!-- 左遮罩 -->
|
||||
<div
|
||||
class="mask-layer absolute left-0 bg-black/60 pointer-events-none z-10"
|
||||
:style="{
|
||||
top: viewfinder.y + 'px',
|
||||
width: viewfinder.x + 'px',
|
||||
height: viewfinder.height + 'px'
|
||||
}"
|
||||
></div>
|
||||
<!-- 右遮罩 -->
|
||||
<div
|
||||
class="mask-layer absolute right-0 bg-black/60 pointer-events-none z-10"
|
||||
:style="{
|
||||
top: viewfinder.y + 'px',
|
||||
left: viewfinder.x + viewfinder.width + 'px',
|
||||
height: viewfinder.height + 'px'
|
||||
}"
|
||||
></div>
|
||||
|
||||
<!-- 可拖动调整的取景框 -->
|
||||
<div
|
||||
ref="viewfinderBox"
|
||||
class="viewfinder-frame absolute border-4 border-white cursor-move z-20"
|
||||
:style="{
|
||||
left: viewfinder.x + 'px',
|
||||
top: viewfinder.y + 'px',
|
||||
width: viewfinder.width + 'px',
|
||||
height: viewfinder.height + 'px'
|
||||
}"
|
||||
@mousedown="handleStartDrag"
|
||||
@touchstart="handleStartDrag"
|
||||
>
|
||||
<!-- 九宫格辅助线 -->
|
||||
<div class="grid-lines absolute inset-0 grid grid-cols-3 grid-rows-3">
|
||||
<div
|
||||
v-for="i in 9"
|
||||
:key="i"
|
||||
class="border border-white/40"
|
||||
:class="{
|
||||
'border-t-0': i <= 3,
|
||||
'border-b-0': i >= 7,
|
||||
'border-l-0': (i - 1) % 3 === 0,
|
||||
'border-r-0': i % 3 === 0
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- 四个角的调整手柄 -->
|
||||
<div
|
||||
class="resize-handle absolute w-6 h-6 bg-white rounded-full -left-3 -top-3 cursor-nwse-resize"
|
||||
@mousedown.stop="handleStartResize('nw', $event)"
|
||||
@touchstart.stop="handleStartResize('nw', $event)"
|
||||
></div>
|
||||
<div
|
||||
class="resize-handle absolute w-6 h-6 bg-white rounded-full -right-3 -top-3 cursor-nesw-resize"
|
||||
@mousedown.stop="handleStartResize('ne', $event)"
|
||||
@touchstart.stop="handleStartResize('ne', $event)"
|
||||
></div>
|
||||
<div
|
||||
class="resize-handle absolute w-6 h-6 bg-white rounded-full -left-3 -bottom-3 cursor-nesw-resize"
|
||||
@mousedown.stop="handleStartResize('sw', $event)"
|
||||
@touchstart.stop="handleStartResize('sw', $event)"
|
||||
></div>
|
||||
<div
|
||||
class="resize-handle absolute w-6 h-6 bg-white rounded-full -right-3 -bottom-3 cursor-nwse-resize"
|
||||
@mousedown.stop="handleStartResize('se', $event)"
|
||||
@touchstart.stop="handleStartResize('se', $event)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部取景按钮 - 跟随取景框 -->
|
||||
<div
|
||||
class="capture-button-container absolute z-20 -translate-x-1/2 -translate-y-1/2"
|
||||
:style="{
|
||||
left: viewfinder.x + viewfinder.width / 2 + 'px',
|
||||
top: viewfinder.y + viewfinder.height + 'px'
|
||||
}"
|
||||
>
|
||||
<ImgButton class="w-36" image="shooting/按钮-取景" @click="captureScene" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果弹窗 -->
|
||||
<Overlay :visible="showModal" @close="handleModalClose">
|
||||
<div class="relative w-[1000px] h-[600px] flex items-center justify-center">
|
||||
<!-- 截图内容 -->
|
||||
<div v-if="capturedImageUrl" class="bg-white p-2 rounded-md -rotate-6">
|
||||
<img :src="capturedImageUrl" alt="拍摄结果" />
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<!-- <div class="absolute bottom-1/2 -right-1/3 translate-y-1/2">
|
||||
<ImgButton image="shooting/按钮-前往下一个取景地" @click="handleFinish" />
|
||||
</div> -->
|
||||
</div>
|
||||
</Overlay>
|
||||
</div>
|
||||
|
||||
<LoadingOverlay :visible="isLoading" text="正在出片中..." />
|
||||
|
||||
<!-- 引导步骤 -->
|
||||
<GuideSteps v-if="showGuide" positionOffsetY="80%" @complete="handleComplete">
|
||||
<GuideStepsItem> 实地取景已全部完成,现在跟着我走进现场开始正式的拍摄吧~ </GuideStepsItem>
|
||||
</GuideSteps>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,291 @@
|
||||
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<HTMLElement>()
|
||||
const viewfinderBox = ref<HTMLElement>()
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user