feat: 讲解模式新增缩放
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
This commit is contained in:
parent
97acaf1d6f
commit
0fd4d0d39c
@ -157,6 +157,59 @@ function onResizeStart(e: MouseEvent) {
|
||||
document.addEventListener('mouseup', onMouseUp)
|
||||
}
|
||||
|
||||
// ======================== 讲解区缩放 ========================
|
||||
const MIN_ZOOM_SCALE = 0.7
|
||||
const MAX_ZOOM_SCALE = 2.5
|
||||
const zoomScale = ref(1)
|
||||
const zoomPercent = computed(() => Math.round(zoomScale.value * 100))
|
||||
let pinchStartDistance = 0
|
||||
let pinchStartScale = 1
|
||||
|
||||
function clampZoomScale(scale: number) {
|
||||
return Math.min(MAX_ZOOM_SCALE, Math.max(MIN_ZOOM_SCALE, scale))
|
||||
}
|
||||
|
||||
function getTouchDistance(touches: TouchList) {
|
||||
const first = touches[0]
|
||||
const second = touches[1]
|
||||
if (!first || !second) return 0
|
||||
return Math.hypot(
|
||||
first.clientX - second.clientX,
|
||||
first.clientY - second.clientY,
|
||||
)
|
||||
}
|
||||
|
||||
function onZoomTouchStart(e: TouchEvent) {
|
||||
if (e.touches.length !== 2) return
|
||||
pinchStartDistance = getTouchDistance(e.touches)
|
||||
pinchStartScale = zoomScale.value
|
||||
}
|
||||
|
||||
function onZoomTouchMove(e: TouchEvent) {
|
||||
if (e.touches.length !== 2 || !pinchStartDistance) return
|
||||
e.preventDefault()
|
||||
const distance = getTouchDistance(e.touches)
|
||||
zoomScale.value = clampZoomScale(
|
||||
pinchStartScale * (distance / pinchStartDistance),
|
||||
)
|
||||
}
|
||||
|
||||
function onZoomTouchEnd(e: TouchEvent) {
|
||||
if (e.touches.length >= 2) return
|
||||
pinchStartDistance = 0
|
||||
}
|
||||
|
||||
function onZoomWheel(e: WheelEvent) {
|
||||
if (!e.ctrlKey && !e.metaKey) return
|
||||
e.preventDefault()
|
||||
const delta = e.deltaY > 0 ? -0.08 : 0.08
|
||||
zoomScale.value = clampZoomScale(zoomScale.value + delta)
|
||||
}
|
||||
|
||||
function resetZoom() {
|
||||
zoomScale.value = 1
|
||||
}
|
||||
|
||||
// ======================== 讲解模式 ========================
|
||||
const currentIndex = ref(0)
|
||||
|
||||
@ -172,6 +225,7 @@ function getTitleKey(item: any) {
|
||||
|
||||
async function enterExplaining() {
|
||||
initLeftWidth()
|
||||
resetZoom()
|
||||
phase.value = 'explaining'
|
||||
currentIndex.value = 0
|
||||
await callExplainApi(1)
|
||||
@ -333,13 +387,23 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
|
||||
<!-- ==================== 全屏讲解阶段 ==================== -->
|
||||
<div v-else-if="phase === 'explaining'" class="explain-fullscreen">
|
||||
<div class="explain-main" v-if="currentQuestion">
|
||||
<div
|
||||
v-if="currentQuestion"
|
||||
class="explain-main"
|
||||
:style="{ '--explain-zoom-scale': zoomScale }"
|
||||
@touchstart="onZoomTouchStart"
|
||||
@touchmove="onZoomTouchMove"
|
||||
@touchend="onZoomTouchEnd"
|
||||
@touchcancel="onZoomTouchEnd"
|
||||
@wheel="onZoomWheel"
|
||||
>
|
||||
<!-- 左侧:题目 -->
|
||||
<div class="explain-left" :style="{ flex: `0 0 ${leftWidth}px` }">
|
||||
<div class="explain-panel-header">
|
||||
<span class="panel-title">第 {{ currentIndex + 1 }} 题</span>
|
||||
</div>
|
||||
<div class="explain-panel-body">
|
||||
<div class="explain-zoom-layer">
|
||||
<template
|
||||
v-if="
|
||||
Array.isArray(currentQuestion.xkwTitle?.questionList) &&
|
||||
@ -366,6 +430,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 拖动分隔条 -->
|
||||
<div class="resize-handle" @mousedown="onResizeStart"></div>
|
||||
@ -376,6 +441,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
<span class="panel-title">答案与解析</span>
|
||||
</div>
|
||||
<div class="explain-panel-body">
|
||||
<div class="explain-zoom-layer">
|
||||
<!-- 答案 -->
|
||||
<div class="info-section" v-if="currentQuestion.xkwTitle?.answer">
|
||||
<div class="info-label">【答案】</div>
|
||||
@ -436,6 +502,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<div class="explain-footer">
|
||||
@ -464,6 +531,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<el-button @click="resetZoom">重置缩放 {{ zoomPercent }}%</el-button>
|
||||
<el-button type="danger" @click="endExplaining">结束讲解</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -591,6 +659,8 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
overflow: hidden;
|
||||
touch-action: pan-x pan-y;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.explain-left,
|
||||
@ -636,6 +706,18 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.explain-zoom-layer {
|
||||
zoom: var(--explain-zoom-scale);
|
||||
}
|
||||
|
||||
@supports not (zoom: 1) {
|
||||
.explain-zoom-layer {
|
||||
width: calc(100% / var(--explain-zoom-scale));
|
||||
transform: scale(var(--explain-zoom-scale));
|
||||
transform-origin: left top;
|
||||
}
|
||||
}
|
||||
|
||||
.info-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user