--- name: 切换缩略图显示状态 description: 开启或关闭右侧的画布缩略图导航面板。 author: ymjr version: 1.0.0 license: MIT usage: 运行脚本将在画板右上角弹出一个带当前全局概览的缩略小地图。通过拖拽小地图中的红色取景框,可以快速且大范围地平移主画板视角。再次运行脚本可关闭小地图。 features: - 构建原生的悬浮 DOM 面板 - 维持 `updateImage` 及状态位以通知后台引擎进行重绘 dependencies: - 必须依赖 [Feature-Thumbnail-Engine] 提供底层截图隔离与渲染 autorun: false --- /* ```javascript */ var locales = { zh: { notice_no_view: "请在 Excalidraw 视图中运行此脚本!", notice_closed: "⭕ 已关闭缩略图导航", notice_opened: "✨ 已开启缩略图导航", ui_title: "🗺️ 导航地图" }, en: { notice_no_view: "Please run this script in an Excalidraw view!", notice_closed: "⭕ Thumbnail navigation closed", notice_opened: "✨ Thumbnail navigation opened", ui_title: "🗺️ Navigator" } }; const targetView = ea.targetView; if (!targetView) { new Notice(t("notice_no_view")); return; } const panelId = `ea-thumbnail-panel-${targetView.id}`; const canvasId = `thumbnailCanvas-${targetView.id}`; let panel = document.getElementById(panelId); if (!panel) { const api = ea.getExcalidrawAPI(); const state = api.getAppState(); // 1. 创建容器面板 panel = document.createElement('div'); panel.id = panelId; panel.style.cssText = `position:absolute; top:30px; right:24px; background:var(--background-primary); border:1px solid var(--background-modifier-border); box-shadow:0 10px 30px rgba(0,0,0,0.25); border-radius:8px; z-index:999; display:flex; flex-direction:column; overflow:hidden;`; // 2. 创建可拖拽头部 const header = document.createElement('div'); header.style.cssText = `padding:6px 12px; background:var(--background-secondary); cursor:move; border-bottom:1px solid var(--background-modifier-border); display:flex; justify-content:space-between; align-items:center; user-select:none;`; header.innerHTML = `${t("ui_title")}`; panel.appendChild(header); // 3. 创建内容区及 Canvas const content = document.createElement('div'); content.style.cssText = `background:var(--color-base-00, #ffffff); display:flex; justify-content:center; align-items:center; padding: 4px;`; const thumbnailCanvas = document.createElement("canvas"); thumbnailCanvas.id = canvasId; const canvasWidth = state.width / 6; const canvasHeight = state.height / 6; thumbnailCanvas.style.width = `${canvasWidth}px`; thumbnailCanvas.style.height = `${canvasHeight}px`; thumbnailCanvas.width = canvasWidth; thumbnailCanvas.height = canvasHeight; // 【修改点】鼠标设为 pointer thumbnailCanvas.style.cursor = "pointer"; thumbnailCanvas.updateImage = true; content.appendChild(thumbnailCanvas); panel.appendChild(content); targetView.contentEl.appendChild(panel); // 4. 面板拖拽逻辑 let isDraggingPanel = false, startX, startY, initLeft, initTop; header.onmousedown = (e) => { if(e.target.tagName === 'BUTTON') return; isDraggingPanel = true; startX = e.clientX; startY = e.clientY; const rect = panel.getBoundingClientRect(); // 转换为相对父容器(targetView)的坐标 const parentRect = targetView.contentEl.getBoundingClientRect(); initLeft = rect.left - parentRect.left; initTop = rect.top - parentRect.top; e.preventDefault(); document.onmousemove = (e) => { if(!isDraggingPanel) return; panel.style.left = (initLeft + e.clientX - startX) + 'px'; panel.style.top = (initTop + e.clientY - startY) + 'px'; // 解除 right 的限制,防止冲突 panel.style.right = 'auto'; }; document.onmouseup = () => { isDraggingPanel = false; document.onmousemove = null; document.onmouseup = null; }; }; // 关闭按钮逻辑 header.querySelector('#close-btn').onclick = () => { panel.remove(); new Notice(t("notice_closed")); }; // 强制触发一次绘制渲染 api.updateScene({ elements: api.getSceneElements().slice() }); new Notice(t("notice_opened")); } else { panel.remove(); new Notice(t("notice_closed")); }