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:
@@ -5775,12 +5775,47 @@ void 0;
|
|||||||
let startY = 0;
|
let startY = 0;
|
||||||
let scrollLeft = 0;
|
let scrollLeft = 0;
|
||||||
let scrollTop = 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) => {
|
inner.addEventListener('mousedown', (e) => {
|
||||||
// 只响应左键
|
// 只响应左键
|
||||||
if (e.button !== 0) return;
|
if (e.button !== 0) return;
|
||||||
|
|
||||||
|
// 需求 13.5: 检查是否需要启用拖拽
|
||||||
|
if (!checkDragEnabled()) return;
|
||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
|
// 需求 13.1: 改变鼠标光标为抓手样式
|
||||||
inner.classList.add('dragging');
|
inner.classList.add('dragging');
|
||||||
|
|
||||||
startX = e.pageX - inner.offsetLeft;
|
startX = e.pageX - inner.offsetLeft;
|
||||||
@@ -5788,7 +5823,13 @@ void 0;
|
|||||||
scrollLeft = inner.scrollLeft;
|
scrollLeft = inner.scrollLeft;
|
||||||
scrollTop = inner.scrollTop;
|
scrollTop = inner.scrollTop;
|
||||||
|
|
||||||
|
// 需求 13.2: 禁用文本选择
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 启动 RAF 循环
|
||||||
|
if (!rafId) {
|
||||||
|
rafId = requestAnimationFrame(updateDragPosition);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mousemove', (e) => {
|
document.addEventListener('mousemove', (e) => {
|
||||||
@@ -5798,17 +5839,21 @@ void 0;
|
|||||||
|
|
||||||
const x = e.pageX - inner.offsetLeft;
|
const x = e.pageX - inner.offsetLeft;
|
||||||
const y = e.pageY - inner.offsetTop;
|
const y = e.pageY - inner.offsetTop;
|
||||||
const walkX = (x - startX) * 2;
|
// 更新待处理的位置,由 RAF 循环处理
|
||||||
const walkY = (y - startY) * 2;
|
pendingX = (x - startX) * 2;
|
||||||
|
pendingY = (y - startY) * 2;
|
||||||
inner.scrollLeft = scrollLeft - walkX;
|
|
||||||
inner.scrollTop = scrollTop - walkY;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
document.addEventListener('mouseup', () => {
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
isDragging = false;
|
isDragging = false;
|
||||||
|
// 需求 13.4: 恢复正常光标
|
||||||
inner.classList.remove('dragging');
|
inner.classList.remove('dragging');
|
||||||
|
// 取消 RAF 循环
|
||||||
|
if (rafId) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -5817,6 +5862,13 @@ void 0;
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 需求 13.2: 禁用文本选择
|
||||||
|
inner.addEventListener('selectstart', (e) => {
|
||||||
|
if (isDragging) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 需求 12.5: 双击智能缩放
|
// 需求 12.5: 双击智能缩放
|
||||||
// 双击图表时,如果当前是默认大小则放大到 2 倍,否则重置到 1 倍
|
// 双击图表时,如果当前是默认大小则放大到 2 倍,否则重置到 1 倍
|
||||||
inner.addEventListener('dblclick', (e) => {
|
inner.addEventListener('dblclick', (e) => {
|
||||||
|
|||||||
15
style.css
15
style.css
@@ -1053,7 +1053,6 @@ html.darkmode .mermaid-zoom-controls {
|
|||||||
html.darkmode .mermaid-zoom-level {
|
html.darkmode .mermaid-zoom-level {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 按钮 tooltip */
|
/* 按钮 tooltip */
|
||||||
.mermaid-zoom-btn[title] {
|
.mermaid-zoom-btn[title] {
|
||||||
@@ -1114,16 +1113,28 @@ html.darkmode .mermaid-hint {
|
|||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 拖拽时的光标 */
|
/* 拖拽时的光标和视觉反馈 */
|
||||||
|
/* 需求 13.1, 13.2, 13.4: 优化拖拽视觉反馈 */
|
||||||
.mermaid-container-inner.dragging {
|
.mermaid-container-inner.dragging {
|
||||||
cursor: grabbing !important;
|
cursor: grabbing !important;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mermaid-container-inner:not(.dragging) {
|
.mermaid-container-inner:not(.dragging) {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 拖拽时禁用 SVG 内的文本选择 */
|
||||||
|
.mermaid-container-inner.dragging svg {
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mermaid 淡入动画 */
|
/* Mermaid 淡入动画 */
|
||||||
@keyframes mermaidFadeIn {
|
@keyframes mermaidFadeIn {
|
||||||
from {
|
from {
|
||||||
|
|||||||
Reference in New Issue
Block a user