feat: 添加手势缩放功能至MarkingImageViewerNew组件
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
a92743dda1
commit
bcacba59a1
|
|
@ -3,7 +3,6 @@ import type { KonvaMarkingData } from '../../composables/renderer/useMarkingKonv
|
|||
import type { ExamStudentMarkingQuestionResponse } from '@/api'
|
||||
import { markingSettings } from '../../composables/useMarkingSettings'
|
||||
import { useSmartScale } from '../../composables/useSmartScale'
|
||||
import ScaleControlPanel from '../ScaleControlPanel.vue'
|
||||
import QuestionRenderer from './QuestionRenderer.vue'
|
||||
|
||||
interface Props {
|
||||
|
|
@ -21,6 +20,62 @@ const smartScale = useSmartScale(props.imageSize)
|
|||
|
||||
const questionData = defineModel<ExamStudentMarkingQuestionResponse[]>('questionData')
|
||||
|
||||
// 手势缩放相关
|
||||
const containerRef = ref<HTMLElement>()
|
||||
const initialDistance = ref(0)
|
||||
const initialScale = ref(1)
|
||||
const isGesturing = ref(false)
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离
|
||||
*/
|
||||
function getDistance(touch1: Touch, touch2: Touch): number {
|
||||
const dx = touch2.clientX - touch1.clientX
|
||||
const dy = touch2.clientY - touch1.clientY
|
||||
return Math.sqrt(dx * dx + dy * dy)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理触摸开始
|
||||
*/
|
||||
function handleTouchStart(e: TouchEvent) {
|
||||
if (e.touches.length === 2) {
|
||||
// 双指触摸,开始缩放手势
|
||||
isGesturing.value = true
|
||||
initialDistance.value = getDistance(e.touches[0], e.touches[1])
|
||||
initialScale.value = smartScale.userScaleFactor.value
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理触摸移动
|
||||
*/
|
||||
function handleTouchMove(e: TouchEvent) {
|
||||
if (e.touches.length === 2 && isGesturing.value) {
|
||||
// 计算新的距离
|
||||
const currentDistance = getDistance(e.touches[0], e.touches[1])
|
||||
|
||||
// 计算缩放比例变化
|
||||
const scaleChange = currentDistance / initialDistance.value
|
||||
|
||||
// 应用新的缩放比例
|
||||
const newScale = initialScale.value * scaleChange
|
||||
smartScale.setScale(newScale)
|
||||
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理触摸结束
|
||||
*/
|
||||
function handleTouchEnd(e: TouchEvent) {
|
||||
if (e.touches.length < 2) {
|
||||
isGesturing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 计算题目布局类
|
||||
const questionsLayoutClass = computed(() => ({
|
||||
'questions-vertical': true, // 多题总是竖排
|
||||
|
|
@ -51,17 +106,20 @@ function handleMarkingChange(questionIndex: number, imageIndex: number, data: Ko
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="multi-question-renderer">
|
||||
<!-- 缩放控制面板 -->
|
||||
<ScaleControlPanel
|
||||
:scale-text="smartScale.scaleText.value"
|
||||
:can-zoom-in="smartScale.canZoomIn.value"
|
||||
:can-zoom-out="smartScale.canZoomOut.value"
|
||||
@zoom-in="smartScale.zoomIn"
|
||||
@zoom-out="smartScale.zoomOut"
|
||||
@reset-zoom="smartScale.resetZoom"
|
||||
@fit-to-screen="smartScale.fitToScreen"
|
||||
/>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="multi-question-renderer"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
@touchend="handleTouchEnd"
|
||||
>
|
||||
<!-- 缩放提示 -->
|
||||
<div
|
||||
v-if="isGesturing"
|
||||
class="fixed left-1/2 top-1/2 z-50 rounded-lg bg-black/70 px-4 py-2 text-white -translate-x-1/2 -translate-y-1/2"
|
||||
>
|
||||
{{ smartScale.scaleText.value }}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col flex-nowrap" :class="questionsLayoutClass">
|
||||
<QuestionRenderer
|
||||
|
|
@ -85,6 +143,7 @@ function handleMarkingChange(questionIndex: number, imageIndex: number, data: Ko
|
|||
overflow: auto;
|
||||
padding: 8rpx 16rpx;
|
||||
padding-bottom: 96rpx;
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
|
||||
.questions-container {
|
||||
|
|
|
|||
|
|
@ -1,50 +1,36 @@
|
|||
import { ref, computed, watch } from 'vue'
|
||||
import { useScreenWidth } from './useScreenWidth'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
/**
|
||||
* 智能缩放控制
|
||||
* 智能缩放控制 - 支持手势缩放
|
||||
*/
|
||||
export function useSmartScale(imageSize: number = 100) {
|
||||
const { screenWidth } = useScreenWidth()
|
||||
|
||||
// 基础缩放比例
|
||||
const baseScale = computed(() => imageSize / 100)
|
||||
|
||||
// 用户手动调节的缩放因子(1.0 = 默认,0.5 = 缩小50%,1.5 = 放大50%)
|
||||
// 用户手动调节的缩放因子(1.0 = 默认100%,0.5 = 缩小50%,2.0 = 放大200%)
|
||||
const userScaleFactor = ref(1.0)
|
||||
|
||||
// 自动适应屏幕的缩放因子
|
||||
const autoScaleFactor = computed(() => {
|
||||
// 当屏幕宽度小于600px时,适当缩小以适应屏幕
|
||||
if (screenWidth.value < 600) {
|
||||
return Math.max(0.6, screenWidth.value / 1000) // 最小0.6倍
|
||||
}
|
||||
return 1.0
|
||||
})
|
||||
|
||||
// 最终的缩放比例
|
||||
// 最终的缩放比例 - 默认100%
|
||||
const finalScale = computed(() => {
|
||||
return baseScale.value * userScaleFactor.value * autoScaleFactor.value
|
||||
return userScaleFactor.value
|
||||
})
|
||||
|
||||
// 缩放控制方法
|
||||
// 设置缩放比例(用于手势缩放)
|
||||
const setScale = (scale: number) => {
|
||||
// 限制缩放范围:0.3倍到5倍
|
||||
userScaleFactor.value = Math.max(0.3, Math.min(5.0, scale))
|
||||
}
|
||||
|
||||
// 缩放控制方法(保留用于可能的按钮控制)
|
||||
const zoomIn = () => {
|
||||
userScaleFactor.value = Math.min(userScaleFactor.value + 0.2, 3.0) // 最大3倍
|
||||
userScaleFactor.value = Math.min(userScaleFactor.value + 0.2, 5.0)
|
||||
}
|
||||
|
||||
const zoomOut = () => {
|
||||
userScaleFactor.value = Math.max(userScaleFactor.value - 0.2, 0.3) // 最小0.3倍
|
||||
userScaleFactor.value = Math.max(userScaleFactor.value - 0.2, 0.3)
|
||||
}
|
||||
|
||||
const resetZoom = () => {
|
||||
userScaleFactor.value = 1.0
|
||||
}
|
||||
|
||||
// 自动适应屏幕
|
||||
const fitToScreen = () => {
|
||||
userScaleFactor.value = 1.0 / autoScaleFactor.value
|
||||
}
|
||||
|
||||
// 缩放级别文本
|
||||
const scaleText = computed(() => {
|
||||
const percentage = Math.round(userScaleFactor.value * 100)
|
||||
|
|
@ -52,19 +38,18 @@ export function useSmartScale(imageSize: number = 100) {
|
|||
})
|
||||
|
||||
// 是否可以放大/缩小
|
||||
const canZoomIn = computed(() => userScaleFactor.value < 3.0)
|
||||
const canZoomIn = computed(() => userScaleFactor.value < 5.0)
|
||||
const canZoomOut = computed(() => userScaleFactor.value > 0.3)
|
||||
|
||||
return {
|
||||
finalScale,
|
||||
userScaleFactor,
|
||||
autoScaleFactor,
|
||||
scaleText,
|
||||
canZoomIn,
|
||||
canZoomOut,
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
resetZoom,
|
||||
fitToScreen,
|
||||
setScale,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue