feat: 优化 Mermaid 拖拽功能

- 7.1 改进拖拽响应:使用 requestAnimationFrame 优化性能,避免频繁的 DOM 操作
- 7.2 优化拖拽视觉反馈:增强光标样式,添加跨浏览器的文本选择禁用
- 7.3 智能启用拖拽:检测图表是否需要拖拽,未缩放且完全可见时自动禁用

需求:13.1, 13.2, 13.3, 13.4, 13.5
This commit is contained in:
2026-01-25 01:30:21 +08:00
parent 39d340fb49
commit 66df8348be
2 changed files with 70 additions and 7 deletions

View File

@@ -5775,12 +5775,47 @@ void 0;
let startY = 0;
let scrollLeft = 0;
let scrollTop = 0;
let rafId = null;
let pendingX = 0;
let pendingY = 0;
// 需求 13.5: 智能启用拖拽 - 检查图表是否需要拖拽
const checkDragEnabled = () => {
const svgElement = inner.querySelector('svg');
if (!svgElement) return false;
// 如果图表未缩放且完全可见,禁用拖拽
const containerRect = container.getBoundingClientRect();
const svgRect = svgElement.getBoundingClientRect();
const isFullyVisible = svgRect.width <= containerRect.width &&
svgRect.height <= containerRect.height;
const isNotZoomed = Math.abs(scale - 1) < 0.01;
return !(isFullyVisible && isNotZoomed);
};
// 需求 13.3: 使用 requestAnimationFrame 优化拖拽性能
const updateDragPosition = () => {
if (!isDragging) {
rafId = null;
return;
}
inner.scrollLeft = scrollLeft - pendingX;
inner.scrollTop = scrollTop - pendingY;
rafId = requestAnimationFrame(updateDragPosition);
};
inner.addEventListener('mousedown', (e) => {
// 只响应左键
if (e.button !== 0) return;
// 需求 13.5: 检查是否需要启用拖拽
if (!checkDragEnabled()) return;
isDragging = true;
// 需求 13.1: 改变鼠标光标为抓手样式
inner.classList.add('dragging');
startX = e.pageX - inner.offsetLeft;
@@ -5788,7 +5823,13 @@ void 0;
scrollLeft = inner.scrollLeft;
scrollTop = inner.scrollTop;
// 需求 13.2: 禁用文本选择
e.preventDefault();
// 启动 RAF 循环
if (!rafId) {
rafId = requestAnimationFrame(updateDragPosition);
}
});
document.addEventListener('mousemove', (e) => {
@@ -5798,17 +5839,21 @@ void 0;
const x = e.pageX - inner.offsetLeft;
const y = e.pageY - inner.offsetTop;
const walkX = (x - startX) * 2;
const walkY = (y - startY) * 2;
inner.scrollLeft = scrollLeft - walkX;
inner.scrollTop = scrollTop - walkY;
// 更新待处理的位置,由 RAF 循环处理
pendingX = (x - startX) * 2;
pendingY = (y - startY) * 2;
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
// 需求 13.4: 恢复正常光标
inner.classList.remove('dragging');
// 取消 RAF 循环
if (rafId) {
cancelAnimationFrame(rafId);
rafId = null;
}
}
});
@@ -5817,6 +5862,13 @@ void 0;
e.preventDefault();
});
// 需求 13.2: 禁用文本选择
inner.addEventListener('selectstart', (e) => {
if (isDragging) {
e.preventDefault();
}
});
// 需求 12.5: 双击智能缩放
// 双击图表时,如果当前是默认大小则放大到 2 倍,否则重置到 1 倍
inner.addEventListener('dblclick', (e) => {

View File

@@ -1053,7 +1053,6 @@ html.darkmode .mermaid-zoom-controls {
html.darkmode .mermaid-zoom-level {
color: #aaa;
}
}
/* 按钮 tooltip */
.mermaid-zoom-btn[title] {
@@ -1114,16 +1113,28 @@ html.darkmode .mermaid-hint {
backdrop-filter: blur(10px);
}
/* 拖拽时的光标 */
/* 拖拽时的光标和视觉反馈 */
/* 需求 13.1, 13.2, 13.4: 优化拖拽视觉反馈 */
.mermaid-container-inner.dragging {
cursor: grabbing !important;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
.mermaid-container-inner:not(.dragging) {
cursor: grab;
}
/* 拖拽时禁用 SVG 内的文本选择 */
.mermaid-container-inner.dragging svg {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
/* Mermaid 淡入动画 */
@keyframes mermaidFadeIn {
from {