feat: 优化 Mermaid 响应式设计
- 移动端工具栏适配:按钮放大至 40px,便于触摸操作 - 双指缩放手势:支持双指捏合缩放,以触摸中心为缩放点 - 单指拖拽移动:支持单指拖拽平移图表 - 触摸事件优化:使用 requestAnimationFrame 提升响应速度 - 横屏模式优化:限制图表高度,工具栏更紧凑,隐藏提示文本 - 添加触摸距离和中心点计算辅助函数 - 完善触摸状态管理(touchstart/touchmove/touchend/touchcancel) 需求:16.1, 16.2, 16.3, 16.4, 16.5
This commit is contained in:
@@ -41,16 +41,16 @@
|
|||||||
- [x] 5.2 优化工具栏样式(半透明背景) _需求:11.3-11.5_
|
- [x] 5.2 优化工具栏样式(半透明背景) _需求:11.3-11.5_
|
||||||
- [x] 5.3 添加按钮提示(tooltip) _需求:20.2_
|
- [x] 5.3 添加按钮提示(tooltip) _需求:20.2_
|
||||||
|
|
||||||
- [~] 6. 增强 Mermaid 缩放功能
|
- [x] 6. 增强 Mermaid 缩放功能
|
||||||
- [ ] 6.1 实现以鼠标为中心的缩放 _需求:12.1_
|
- [x] 6.1 实现以鼠标为中心的缩放 _需求:12.1_
|
||||||
- [ ] 6.2 优化缩放动画(CSS transform + transition) _需求:12.2, 12.3_
|
- [x] 6.2 优化缩放动画(CSS transform + transition) _需求:12.2, 12.3_
|
||||||
- [ ] 6.3 实现智能缩放(双击) _需求:12.5_
|
- [x] 6.3 实现智能缩放(双击) _需求:12.5_
|
||||||
- [ ] 6.4 优化缩放按钮状态 _需求:12.4, 20.3_
|
- [x] 6.4 优化缩放按钮状态 _需求:12.4, 20.3_
|
||||||
|
|
||||||
- [~] 7. 优化 Mermaid 拖拽功能
|
- [x] 7. 优化 Mermaid 拖拽功能
|
||||||
- [ ] 7.1 改进拖拽响应(requestAnimationFrame) _需求:13.3_
|
- [x] 7.1 改进拖拽响应(requestAnimationFrame) _需求:13.3_
|
||||||
- [ ] 7.2 优化拖拽视觉反馈 _需求:13.1, 13.2, 13.4_
|
- [x] 7.2 优化拖拽视觉反馈 _需求:13.1, 13.2, 13.4_
|
||||||
- [ ] 7.3 智能启用拖拽 _需求:13.5_
|
- [x] 7.3 智能启用拖拽 _需求:13.5_
|
||||||
|
|
||||||
### 阶段 3: Mermaid 高级功能
|
### 阶段 3: Mermaid 高级功能
|
||||||
|
|
||||||
@@ -65,10 +65,10 @@
|
|||||||
- [ ] 9.3 实现 SVG 导出 _需求:15.3, 15.4_
|
- [ ] 9.3 实现 SVG 导出 _需求:15.3, 15.4_
|
||||||
- [ ] 9.4 添加导出错误处理 _需求:15.5_
|
- [ ] 9.4 添加导出错误处理 _需求:15.5_
|
||||||
|
|
||||||
- [ ] 10. 优化 Mermaid 响应式设计
|
- [x] 10. 优化 Mermaid 响应式设计
|
||||||
- [ ] 10.1 移动端工具栏适配 _需求:16.1_
|
- [x] 10.1 移动端工具栏适配 _需求:16.1_
|
||||||
- [ ] 10.2 实现触摸手势支持 _需求:16.2-16.4_
|
- [x] 10.2 实现触摸手势支持 _需求:16.2-16.4_
|
||||||
- [ ] 10.3 横屏模式优化 _需求:16.5_
|
- [x] 10.3 横屏模式优化 _需求:16.5_
|
||||||
|
|
||||||
### 阶段 4: Mermaid 主题和性能优化
|
### 阶段 4: Mermaid 主题和性能优化
|
||||||
|
|
||||||
@@ -77,16 +77,16 @@
|
|||||||
- [x] 11.2 保持图表状态 _需求:17.3_
|
- [x] 11.2 保持图表状态 _需求:17.3_
|
||||||
- [x] 11.3 添加主题切换动画 _需求:17.4, 17.5_
|
- [x] 11.3 添加主题切换动画 _需求:17.4, 17.5_
|
||||||
|
|
||||||
- [~] 12. 优化 Mermaid 渲染性能
|
- [x] 12. 优化 Mermaid 渲染性能
|
||||||
- [ ] 12.1 实现批量渲染 _需求:18.1_
|
- [x] 12.1 实现批量渲染 _需求:18.1_
|
||||||
- [ ] 12.2 添加加载动画 _需求:18.2, 18.3_
|
- [x] 12.2 添加加载动画 _需求:18.2, 18.3_
|
||||||
- [ ] 12.3 实现延迟渲染 _需求:18.4_
|
- [x] 12.3 实现延迟渲染 _需求:18.4_
|
||||||
- [ ] 12.4 优化错误处理 _需求:18.5_
|
- [x] 12.4 优化错误处理 _需求:18.5_
|
||||||
|
|
||||||
- [~] 13. 优化 Mermaid 错误提示
|
- [x] 13. 优化 Mermaid 错误提示
|
||||||
- [ ] 13.1 设计友好的错误容器 _需求:19.1, 19.2, 19.4_
|
- [x] 13.1 设计友好的错误容器 _需求:19.1, 19.2, 19.4_
|
||||||
- [ ] 13.2 添加原始代码查看 _需求:19.3_
|
- [x] 13.2 添加原始代码查看 _需求:19.3_
|
||||||
- [ ] 13.3 夜间模式适配 _需求:19.5_
|
- [x] 13.3 夜间模式适配 _需求:19.5_
|
||||||
|
|
||||||
### 阶段 5: 测试和优化
|
### 阶段 5: 测试和优化
|
||||||
|
|
||||||
|
|||||||
141
argontheme.js
141
argontheme.js
@@ -5931,6 +5931,147 @@ void 0;
|
|||||||
container.scrollTop = offsetY * scale - mouseY;
|
container.scrollTop = offsetY * scale - mouseY;
|
||||||
}, 50);
|
}, 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 });
|
||||||
},
|
},
|
||||||
|
|
||||||
// ---------- 主题切换监听 ----------
|
// ---------- 主题切换监听 ----------
|
||||||
|
|||||||
59
style.css
59
style.css
@@ -1152,7 +1152,8 @@ article.card .mermaid-container {
|
|||||||
margin: 20px -20px;
|
margin: 20px -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 响应式调<EFBFBD>?*/
|
/* 响应式调整 */
|
||||||
|
/* 需求 16.1: 移动端工具栏适配 - 调整按钮大小 */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.mermaid-container {
|
.mermaid-container {
|
||||||
margin: 15px -15px;
|
margin: 15px -15px;
|
||||||
@@ -1162,6 +1163,62 @@ article.card .mermaid-container {
|
|||||||
article.card .mermaid-container {
|
article.card .mermaid-container {
|
||||||
margin: 15px -15px;
|
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) {
|
@media screen and (max-width: 480px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user