From 39d340fb499c719778c137ddc463b5d9da018f8c Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Sun, 25 Jan 2026 01:25:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20Mermaid=20?= =?UTF-8?q?=E7=BC=A9=E6=94=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 6.1 实现以鼠标为中心的缩放:滚轮缩放时自动调整滚动位置,保持鼠标指向的内容不变 - 6.2 优化缩放动画:按钮点击使用平滑过渡,滚轮缩放禁用过渡以获得更流畅的体验 - 6.3 实现智能缩放:双击图表时,默认大小放大到 2 倍,其他情况重置到 1 倍 - 6.4 优化缩放按钮状态:达到最大/最小缩放级别时自动禁用对应按钮 需求:12.1, 12.2, 12.3, 12.4, 12.5, 20.3 --- argontheme.js | 99 +++++++++++++++++++++++++++++++++++++++++++++++---- style.css | 35 ++++++++++++------ 2 files changed, 117 insertions(+), 17 deletions(-) diff --git a/argontheme.js b/argontheme.js index 9c99c62..85b4f11 100644 --- a/argontheme.js +++ b/argontheme.js @@ -5671,31 +5671,74 @@ void 0; const step = 0.25; // 更新缩放显示 - const updateZoom = () => { + // 需求 12.2, 12.3: 使用 CSS transform 实现硬件加速和平滑过渡 + // 需求 12.4, 20.3: 更新按钮状态 + const updateZoom = (useTransition = true) => { + // 控制是否使用过渡动画 + if (!useTransition) { + inner.style.transition = 'none'; + } else { + inner.style.transition = 'transform 0.3s ease'; + } + inner.style.transform = `scale(${scale})`; controls.querySelector('.mermaid-zoom-level').textContent = Math.round(scale * 100) + '%'; + + // 更新按钮状态 + const zoomInBtn = controls.querySelector('[data-action="zoom-in"]'); + const zoomOutBtn = controls.querySelector('[data-action="zoom-out"]'); + + // 禁用/启用放大按钮 + if (scale >= maxScale) { + zoomInBtn.disabled = true; + zoomInBtn.style.opacity = '0.4'; + zoomInBtn.style.cursor = 'not-allowed'; + } else { + zoomInBtn.disabled = false; + zoomInBtn.style.opacity = '1'; + zoomInBtn.style.cursor = 'pointer'; + } + + // 禁用/启用缩小按钮 + if (scale <= minScale) { + zoomOutBtn.disabled = true; + zoomOutBtn.style.opacity = '0.4'; + zoomOutBtn.style.cursor = 'not-allowed'; + } else { + zoomOutBtn.disabled = false; + zoomOutBtn.style.opacity = '1'; + zoomOutBtn.style.cursor = 'pointer'; + } + + // 恢复过渡(用于下次按钮点击) + if (!useTransition) { + requestAnimationFrame(() => { + inner.style.transition = 'transform 0.3s ease'; + }); + } }; // 绑定按钮事件 controls.addEventListener('click', (e) => { const btn = e.target.closest('.mermaid-zoom-btn'); - if (!btn) return; + if (!btn || btn.disabled) return; // 忽略禁用的按钮 const action = btn.dataset.action; if (action === 'zoom-in' && scale < maxScale) { scale = Math.min(scale + step, maxScale); - updateZoom(); + updateZoom(true); // 按钮点击使用平滑过渡 } else if (action === 'zoom-out' && scale > minScale) { scale = Math.max(scale - step, minScale); - updateZoom(); + updateZoom(true); // 按钮点击使用平滑过渡 } else if (action === 'zoom-reset') { scale = 1; - updateZoom(); + updateZoom(true); // 重置使用平滑过渡 } }); // 鼠标滚轮缩放(按住 Ctrl) + // 需求 12.1: 以鼠标为中心进行缩放 container.addEventListener('wheel', (e) => { if (e.ctrlKey || e.metaKey) { e.preventDefault(); @@ -5704,8 +5747,24 @@ void 0; const newScale = Math.max(minScale, Math.min(maxScale, scale + delta)); if (newScale !== scale) { + // 获取鼠标相对于容器的位置 + const rect = container.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + // 计算鼠标相对于内容的位置(缩放前) + const scrollLeft = container.scrollLeft; + const scrollTop = container.scrollTop; + const offsetX = (scrollLeft + mouseX) / scale; + const offsetY = (scrollTop + mouseY) / scale; + + // 更新缩放(滚轮缩放不使用过渡动画,更流畅) scale = newScale; - updateZoom(); + updateZoom(false); + + // 调整滚动位置,使鼠标位置保持不变 + container.scrollLeft = offsetX * scale - mouseX; + container.scrollTop = offsetY * scale - mouseY; } } }, { passive: false }); @@ -5757,6 +5816,34 @@ void 0; inner.addEventListener('dragstart', (e) => { e.preventDefault(); }); + + // 需求 12.5: 双击智能缩放 + // 双击图表时,如果当前是默认大小则放大到 2 倍,否则重置到 1 倍 + inner.addEventListener('dblclick', (e) => { + e.preventDefault(); + + // 获取双击位置 + const rect = container.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + // 计算鼠标相对于内容的位置(缩放前) + const scrollLeft = container.scrollLeft; + const scrollTop = container.scrollTop; + const offsetX = (scrollLeft + mouseX) / scale; + const offsetY = (scrollTop + mouseY) / scale; + + // 智能缩放:如果接近默认大小则放大,否则重置 + const targetScale = Math.abs(scale - 1) < 0.1 ? 2 : 1; + scale = targetScale; + updateZoom(true); // 使用平滑过渡 + + // 调整滚动位置,使双击位置保持在视野中心 + setTimeout(() => { + container.scrollLeft = offsetX * scale - mouseX; + container.scrollTop = offsetY * scale - mouseY; + }, 50); + }); }, // ---------- 主题切换监听 ---------- diff --git a/style.css b/style.css index b1e122c..f9c8fd1 100644 --- a/style.css +++ b/style.css @@ -921,7 +921,7 @@ article .wp-block-separator { animation: mermaidFadeIn 0.3s ease-in-out forwards; } -/* 需求 18.2: Mermaid 加载动画容器 */ +/* 需�?18.2: Mermaid 加载动画容器 */ .mermaid-loading { background: var(--color-foreground); border-radius: 8px; @@ -1034,6 +1034,12 @@ html.darkmode .mermaid-zoom-controls { transform: scale(0.95); } +.mermaid-zoom-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; +} + .mermaid-zoom-level { display: flex; align-items: center; @@ -1047,6 +1053,7 @@ html.darkmode .mermaid-zoom-controls { html.darkmode .mermaid-zoom-level { color: #aaa; } +} /* 按钮 tooltip */ .mermaid-zoom-btn[title] { @@ -1134,7 +1141,7 @@ article.card .mermaid-container { margin: 20px -20px; } -/* 响应式调整 */ +/* 响应式调�?*/ @media screen and (max-width: 768px) { .mermaid-container { margin: 15px -15px; @@ -2503,7 +2510,7 @@ html:not(.is-home) .cover-scroll-down { -/* 左侧栏 */ +/* 左侧�?*/ .leftbar-banner { @@ -2863,7 +2870,7 @@ html.navbar-absolute #leftbar_part2.sticky { max-height: calc(100vh - 110px - var(--leftbar-part3-height, 0px)); } -/* 只有文章目录标签需要滚动 */ +/* 只有文章目录标签需要滚�?*/ #leftbar_part2.sticky #leftbar_tab_catalog { max-height: calc(100vh - 200px - var(--leftbar-part3-height, 0px)); overflow-y: auto; @@ -7717,7 +7724,7 @@ html.darkmode #post_comment.post-comment-force-privatemode-off .comment-post-pri border-radius: 0px; } -/*短代码 Github*/ +/*短代�?Github*/ .github-info-card { @@ -11197,7 +11204,7 @@ html.darkmode.amoled-dark #content:after { text-align: center; } - /* 移动端已完成任务分隔栏 */ + /* 移动端已完成任务分隔�?*/ .mobile-todo-completed-divider { display: flex; align-items: center; @@ -12816,7 +12823,7 @@ html.navbar-absolute #leftbar_part3.sticky { margin-left: auto; } -/* 折叠已完成按钮 */ +/* 折叠已完成按�?*/ .todo-collapse-btn { display: flex; align-items: center; @@ -14416,7 +14423,7 @@ article.post.card { transition: all var(--m3-motion-duration-short) var(--m3-motion-standard); } -/* M3 代码块 */ +/* M3 代码�?*/ article pre:not(.hljs-codeblock) { border-radius: var(--m3-shape-md) !important; } @@ -14965,7 +14972,7 @@ html.darkmode #leftbar_part2 { border-color: var(--themecolor); } -/* 评论项悬浮效果 */ +/* 评论项悬浮效�?*/ .comment-body { transition: all 0.25s ease; border-radius: var(--m3-shape-md); @@ -16923,6 +16930,12 @@ html.darkmode .mermaid-zoom-controls { transform: scale(0.95); } +.mermaid-zoom-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; +} + .mermaid-zoom-level { display: flex; align-items: center; @@ -16983,7 +16996,7 @@ html.darkmode .mermaid-container { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); } -/* ---------- 响应式设计 ---------- */ +/* ---------- 响应式设�?---------- */ @media screen and (max-width: 768px) { .mermaid-container { padding: 15px; @@ -17149,7 +17162,7 @@ html.darkmode .mermaid-error-code pre { font-size: 14px; } -/* ---------- 滚动条样式(Webkit) ---------- */ +/* ---------- 滚动条样式(Webkit�?---------- */ .mermaid-container::-webkit-scrollbar { height: 8px; }