hookehuyr

feat(PdfViewer): 添加控制栏拖动功能

为PDF查看器添加控制栏拖动功能,支持鼠标和触摸屏操作
添加阻尼效果和拖动阈值,优化用户体验
更新样式以反映拖动状态
......@@ -52,6 +52,7 @@ declare module 'vue' {
VanDropdownMenu: typeof import('vant/es')['DropdownMenu']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanFloatingBubble: typeof import('vant/es')['FloatingBubble']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
......
<!--
* @Date: 2025-01-21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-10-22 13:49:33
* @LastEditTime: 2025-10-22 14:11:50
* @FilePath: /mlaj/src/components/ui/PdfViewer.vue
* @Description: PDF预览组件 - 使用pdf-vue3库
-->
......@@ -10,7 +10,7 @@
:style="{ height: '100%', width: '100%' }">
<div class="pdf-viewer-container">
<!-- PDF内容区域 -->
<div class="pdf-content" :class="{ 'pdf-no-select': preventSave }">
<div id="pdf-content" class="pdf-content" :class="{ 'pdf-no-select': preventSave }">
<PDF v-if="url && show && !loadingError"
ref="pdfRef"
:src="url"
......@@ -53,7 +53,16 @@
<!-- 已移除顶部关闭按钮,关闭功能迁移至控制栏 -->
<!-- 缩放控制按钮 -->
<div v-if="!loading && !loadingError" class="zoom-controls">
<div
v-if="!loading && !loadingError"
class="zoom-controls"
:class="{ 'dragging': isDragging }"
:style="{ bottom: controlsY + 'px' }"
@mousedown="handleDragStart"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
>
<van-button class="zoom-btn zoom-close-btn" type="default" icon="cross" round @click="handleClose" />
<!-- <van-button class="zoom-btn zoom-in-btn" type="default" icon="plus" round @click="zoomIn" /> -->
<font-awesome-icon icon="magnifying-glass-plus" class="text-2xl text-gray-600 ml-2 mr-2" @click="zoomIn" />
......@@ -152,6 +161,14 @@ const pageInput = ref('');
const isEditingPage = ref(false);
const pageInputRef = ref(null);
// 拖动控制相关变量
const isDragging = ref(false); // 是否正在拖动
const dragStartY = ref(0); // 拖动开始时的Y坐标
const controlsY = ref(20); // 控制栏距离底部的距离(px)
const dragOffset = ref(0); // 拖动偏移量
const initialControlsY = ref(20); // 拖动开始时控制栏的初始位置
const dragThreshold = 5; // 拖动阈值,超过这个距离才认为是拖动
/**
* 处理页码输入框失焦事件
*/
......@@ -181,6 +198,110 @@ const focusPageInput = () => {
}
};
/**
* 处理拖动开始事件(鼠标)
*/
const handleDragStart = (e) => {
// 如果点击的是按钮,不处理拖动
if (e.target.closest('.zoom-btn') || e.target.closest('.page-jump')) {
return;
}
dragStartY.value = e.clientY;
initialControlsY.value = controlsY.value; // 记录拖动开始时的位置
dragOffset.value = 0;
document.addEventListener('mousemove', handleDragMove);
document.addEventListener('mouseup', handleDragEnd);
// 不立即调用preventDefault,等确认是拖动后再阻止
};
/**
* 处理拖动移动事件(鼠标)
*/
const handleDragMove = (e) => {
const deltaY = Math.abs(dragStartY.value - e.clientY);
// 只有移动距离超过阈值才开始拖动
if (!isDragging.value && deltaY > dragThreshold) {
isDragging.value = true;
e.preventDefault();
}
if (!isDragging.value) return;
const actualDeltaY = dragStartY.value - e.clientY; // 向上为正,向下为负
const dampingFactor = 0.3; // 阻尼系数,降低移动速度
const dampedDelta = actualDeltaY * dampingFactor;
const newY = Math.max(20, Math.min(window.innerHeight - 100, initialControlsY.value + dampedDelta));
controlsY.value = newY;
dragOffset.value = dampedDelta;
e.preventDefault();
};
/**
* 处理拖动结束事件(鼠标)
*/
const handleDragEnd = () => {
isDragging.value = false;
dragStartY.value = 0;
dragOffset.value = 0;
document.removeEventListener('mousemove', handleDragMove);
document.removeEventListener('mouseup', handleDragEnd);
};
/**
* 处理触摸开始事件(移动端)
*/
const handleTouchStart = (e) => {
if (e.touches.length === 1) {
// 如果触摸的是按钮,不处理拖动
if (e.target.closest('.zoom-btn') || e.target.closest('.page-jump')) {
return;
}
dragStartY.value = e.touches[0].clientY;
initialControlsY.value = controlsY.value; // 记录拖动开始时的位置
dragOffset.value = 0;
// 不立即调用preventDefault,等确认是拖动后再阻止
}
};
/**
* 处理触摸移动事件(移动端)
*/
const handleTouchMove = (e) => {
if (e.touches.length !== 1) return;
const deltaY = Math.abs(dragStartY.value - e.touches[0].clientY);
// 只有移动距离超过阈值才开始拖动
if (!isDragging.value && deltaY > dragThreshold) {
isDragging.value = true;
e.preventDefault();
}
if (!isDragging.value) return;
const actualDeltaY = dragStartY.value - e.touches[0].clientY; // 向上为正,向下为负
const dampingFactor = 0.4; // 移动端稍微高一点的阻尼系数
const dampedDelta = actualDeltaY * dampingFactor;
const newY = Math.max(20, Math.min(window.innerHeight - 100, initialControlsY.value + dampedDelta));
controlsY.value = newY;
dragOffset.value = dampedDelta;
e.preventDefault();
};
/**
* 处理触摸结束事件(移动端)
*/
const handleTouchEnd = () => {
isDragging.value = false;
dragStartY.value = 0;
dragOffset.value = 0;
};
// 超时处理相关
const loadingTimeout = ref(30000); // 超时时间,默认30秒
const timeoutTimer = ref(null); // 超时计时器
......@@ -749,6 +870,8 @@ const applyPinchZoom = (newZoom, centerX, centerY, container) => {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(8px);
transition: all 0.3s ease;
cursor: grab;
user-select: none;
}
.zoom-controls:hover {
......@@ -757,6 +880,14 @@ const applyPinchZoom = (newZoom, centerX, centerY, container) => {
transform: translateX(-50%) translateY(-2px);
}
.zoom-controls.dragging {
cursor: grabbing;
transform: translateX(-50%) scale(1.05);
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.25);
background: rgba(255, 255, 255, 1);
transition: none;
}
.zoom-btn {
width: 40px;
height: 40px;
......