From 1ec2ebc2794dd9dd63a90fd4b86c45e72c0a4c42 Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Sun, 25 Jan 2026 01:45:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20Mermaid=20?= =?UTF-8?q?=E5=93=8D=E5=BA=94=E5=BC=8F=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移动端工具栏适配:按钮放大至 40px,便于触摸操作 - 双指缩放手势:支持双指捏合缩放,以触摸中心为缩放点 - 单指拖拽移动:支持单指拖拽平移图表 - 触摸事件优化:使用 requestAnimationFrame 提升响应速度 - 横屏模式优化:限制图表高度,工具栏更紧凑,隐藏提示文本 - 添加触摸距离和中心点计算辅助函数 - 完善触摸状态管理(touchstart/touchmove/touchend/touchcancel) 需求:16.1, 16.2, 16.3, 16.4, 16.5 --- .kiro/specs/pjax-lazyload-fix/tasks.md | 44 ++++---- argontheme.js | 141 +++++++++++++++++++++++++ style.css | 59 ++++++++++- 3 files changed, 221 insertions(+), 23 deletions(-) diff --git a/.kiro/specs/pjax-lazyload-fix/tasks.md b/.kiro/specs/pjax-lazyload-fix/tasks.md index f512981..90b3209 100644 --- a/.kiro/specs/pjax-lazyload-fix/tasks.md +++ b/.kiro/specs/pjax-lazyload-fix/tasks.md @@ -41,16 +41,16 @@ - [x] 5.2 优化工具栏样式(半透明背景) _需求:11.3-11.5_ - [x] 5.3 添加按钮提示(tooltip) _需求:20.2_ -- [~] 6. 增强 Mermaid 缩放功能 - - [ ] 6.1 实现以鼠标为中心的缩放 _需求:12.1_ - - [ ] 6.2 优化缩放动画(CSS transform + transition) _需求:12.2, 12.3_ - - [ ] 6.3 实现智能缩放(双击) _需求:12.5_ - - [ ] 6.4 优化缩放按钮状态 _需求:12.4, 20.3_ +- [x] 6. 增强 Mermaid 缩放功能 + - [x] 6.1 实现以鼠标为中心的缩放 _需求:12.1_ + - [x] 6.2 优化缩放动画(CSS transform + transition) _需求:12.2, 12.3_ + - [x] 6.3 实现智能缩放(双击) _需求:12.5_ + - [x] 6.4 优化缩放按钮状态 _需求:12.4, 20.3_ -- [~] 7. 优化 Mermaid 拖拽功能 - - [ ] 7.1 改进拖拽响应(requestAnimationFrame) _需求:13.3_ - - [ ] 7.2 优化拖拽视觉反馈 _需求:13.1, 13.2, 13.4_ - - [ ] 7.3 智能启用拖拽 _需求:13.5_ +- [x] 7. 优化 Mermaid 拖拽功能 + - [x] 7.1 改进拖拽响应(requestAnimationFrame) _需求:13.3_ + - [x] 7.2 优化拖拽视觉反馈 _需求:13.1, 13.2, 13.4_ + - [x] 7.3 智能启用拖拽 _需求:13.5_ ### 阶段 3: Mermaid 高级功能 @@ -65,10 +65,10 @@ - [ ] 9.3 实现 SVG 导出 _需求:15.3, 15.4_ - [ ] 9.4 添加导出错误处理 _需求:15.5_ -- [ ] 10. 优化 Mermaid 响应式设计 - - [ ] 10.1 移动端工具栏适配 _需求:16.1_ - - [ ] 10.2 实现触摸手势支持 _需求:16.2-16.4_ - - [ ] 10.3 横屏模式优化 _需求:16.5_ +- [x] 10. 优化 Mermaid 响应式设计 + - [x] 10.1 移动端工具栏适配 _需求:16.1_ + - [x] 10.2 实现触摸手势支持 _需求:16.2-16.4_ + - [x] 10.3 横屏模式优化 _需求:16.5_ ### 阶段 4: Mermaid 主题和性能优化 @@ -77,16 +77,16 @@ - [x] 11.2 保持图表状态 _需求:17.3_ - [x] 11.3 添加主题切换动画 _需求:17.4, 17.5_ -- [~] 12. 优化 Mermaid 渲染性能 - - [ ] 12.1 实现批量渲染 _需求:18.1_ - - [ ] 12.2 添加加载动画 _需求:18.2, 18.3_ - - [ ] 12.3 实现延迟渲染 _需求:18.4_ - - [ ] 12.4 优化错误处理 _需求:18.5_ +- [x] 12. 优化 Mermaid 渲染性能 + - [x] 12.1 实现批量渲染 _需求:18.1_ + - [x] 12.2 添加加载动画 _需求:18.2, 18.3_ + - [x] 12.3 实现延迟渲染 _需求:18.4_ + - [x] 12.4 优化错误处理 _需求:18.5_ -- [~] 13. 优化 Mermaid 错误提示 - - [ ] 13.1 设计友好的错误容器 _需求:19.1, 19.2, 19.4_ - - [ ] 13.2 添加原始代码查看 _需求:19.3_ - - [ ] 13.3 夜间模式适配 _需求:19.5_ +- [x] 13. 优化 Mermaid 错误提示 + - [x] 13.1 设计友好的错误容器 _需求:19.1, 19.2, 19.4_ + - [x] 13.2 添加原始代码查看 _需求:19.3_ + - [x] 13.3 夜间模式适配 _需求:19.5_ ### 阶段 5: 测试和优化 diff --git a/argontheme.js b/argontheme.js index f847c09..01ce2c6 100644 --- a/argontheme.js +++ b/argontheme.js @@ -5931,6 +5931,147 @@ void 0; container.scrollTop = offsetY * scale - mouseY; }, 50); }); + + // ========================================================================== + // 需求 16.2-16.4: 移动端触摸手势支持 + // ========================================================================== + + // 触摸状态 + let touchStartDistance = 0; + let touchStartScale = 1; + let touchStartX = 0; + let touchStartY = 0; + let isTouchDragging = false; + let touchScrollLeft = 0; + let touchScrollTop = 0; + + /** + * 计算两个触摸点之间的距离 + * @param {Touch} touch1 - 第一个触摸点 + * @param {Touch} touch2 - 第二个触摸点 + * @returns {number} 距离 + */ + const getTouchDistance = (touch1, touch2) => { + const dx = touch2.clientX - touch1.clientX; + const dy = touch2.clientY - touch1.clientY; + return Math.sqrt(dx * dx + dy * dy); + }; + + /** + * 获取两个触摸点的中心位置 + * @param {Touch} touch1 - 第一个触摸点 + * @param {Touch} touch2 - 第二个触摸点 + * @returns {Object} 中心位置 {x, y} + */ + const getTouchCenter = (touch1, touch2) => { + return { + x: (touch1.clientX + touch2.clientX) / 2, + y: (touch1.clientY + touch2.clientY) / 2 + }; + }; + + // 需求 16.2: 双指缩放手势 + inner.addEventListener('touchstart', (e) => { + if (e.touches.length === 2) { + // 双指触摸 - 缩放模式 + e.preventDefault(); + + touchStartDistance = getTouchDistance(e.touches[0], e.touches[1]); + touchStartScale = scale; + + // 记录缩放中心点 + const center = getTouchCenter(e.touches[0], e.touches[1]); + const rect = container.getBoundingClientRect(); + touchStartX = center.x - rect.left; + touchStartY = center.y - rect.top; + + } else if (e.touches.length === 1) { + // 需求 16.3: 单指拖拽移动 + // 检查是否需要启用拖拽 + if (!checkDragEnabled()) return; + + isTouchDragging = true; + inner.classList.add('dragging'); + + const touch = e.touches[0]; + touchStartX = touch.clientX; + touchStartY = touch.clientY; + touchScrollLeft = container.scrollLeft; + touchScrollTop = container.scrollTop; + } + }, { passive: false }); + + // 需求 16.4: 优化触摸事件响应速度 + inner.addEventListener('touchmove', (e) => { + if (e.touches.length === 2) { + // 双指缩放 + e.preventDefault(); + + const currentDistance = getTouchDistance(e.touches[0], e.touches[1]); + const scaleChange = currentDistance / touchStartDistance; + const newScale = Math.max(minScale, Math.min(maxScale, touchStartScale * scaleChange)); + + if (newScale !== scale) { + // 获取缩放中心点 + const center = getTouchCenter(e.touches[0], e.touches[1]); + const rect = container.getBoundingClientRect(); + const centerX = center.x - rect.left; + const centerY = center.y - rect.top; + + // 计算缩放前的内容位置 + const scrollLeft = container.scrollLeft; + const scrollTop = container.scrollTop; + const offsetX = (scrollLeft + centerX) / scale; + const offsetY = (scrollTop + centerY) / scale; + + // 更新缩放(不使用过渡动画,更流畅) + scale = newScale; + updateZoom(false); + + // 调整滚动位置,使缩放中心保持不变 + requestAnimationFrame(() => { + container.scrollLeft = offsetX * scale - centerX; + container.scrollTop = offsetY * scale - centerY; + }); + } + + } else if (e.touches.length === 1 && isTouchDragging) { + // 单指拖拽 + e.preventDefault(); + + const touch = e.touches[0]; + const deltaX = touch.clientX - touchStartX; + const deltaY = touch.clientY - touchStartY; + + // 使用 requestAnimationFrame 优化性能 + requestAnimationFrame(() => { + container.scrollLeft = touchScrollLeft - deltaX; + container.scrollTop = touchScrollTop - deltaY; + }); + } + }, { passive: false }); + + inner.addEventListener('touchend', (e) => { + if (isTouchDragging) { + isTouchDragging = false; + inner.classList.remove('dragging'); + } + + // 重置触摸状态 + if (e.touches.length === 0) { + touchStartDistance = 0; + touchStartScale = 1; + } + }, { passive: true }); + + inner.addEventListener('touchcancel', () => { + if (isTouchDragging) { + isTouchDragging = false; + inner.classList.remove('dragging'); + } + touchStartDistance = 0; + touchStartScale = 1; + }, { passive: true }); }, // ---------- 主题切换监听 ---------- diff --git a/style.css b/style.css index c2e0926..8c5a80e 100644 --- a/style.css +++ b/style.css @@ -1152,7 +1152,8 @@ article.card .mermaid-container { margin: 20px -20px; } -/* 响应式调�?*/ +/* 响应式调整 */ +/* 需求 16.1: 移动端工具栏适配 - 调整按钮大小 */ @media screen and (max-width: 768px) { .mermaid-container { margin: 15px -15px; @@ -1162,6 +1163,62 @@ article.card .mermaid-container { article.card .mermaid-container { margin: 15px -15px; } + + /* 移动端工具栏按钮放大,便于触摸 */ + .mermaid-zoom-controls { + padding: 8px; + gap: 8px; + } + + .mermaid-zoom-btn { + width: 40px; + height: 40px; + font-size: 20px; + } + + .mermaid-zoom-level { + font-size: 14px; + min-width: 55px; + padding: 0 10px; + } + + /* 移动端提示文本调整 */ + .mermaid-hint { + font-size: 11px; + padding: 5px 10px; + bottom: 8px; + } +} + +/* 需求 16.5: 横屏模式优化 - 自动调整图表布局 */ +@media screen and (max-width: 768px) and (orientation: landscape) { + .mermaid-container { + max-height: 70vh; + overflow-y: auto; + } + + .mermaid-container-inner { + max-height: 65vh; + } + + /* 横屏时工具栏更紧凑 */ + .mermaid-zoom-controls { + padding: 5px; + gap: 5px; + top: 5px; + right: 5px; + } + + .mermaid-zoom-btn { + width: 36px; + height: 36px; + font-size: 18px; + } + + /* 横屏时隐藏提示文本,节省空间 */ + .mermaid-hint { + display: none; + } } @media screen and (max-width: 480px) {