feat: 添加手势缩放功能至MarkingImageViewerNew组件
continuous-integration/drone/push Build is passing Details

This commit is contained in:
AfyerCu 2025-10-21 09:55:28 +08:00
parent a92743dda1
commit bcacba59a1
2 changed files with 94 additions and 50 deletions

View File

@ -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 {

View File

@ -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,
}
}