mirror of
https://github.com/hugo-fixit/FixIt.git
synced 2026-08-24 15:28:57 +00:00
perf(mermaid): optimize pan/zoom transform handling and improve theme synchronization
This commit is contained in:
+49
-43
@@ -56,6 +56,7 @@ function nextFrame() {
|
||||
* (e.g. `.mermaid` vs `.mermaid-dark`) within the same `.diagram-container`.
|
||||
*
|
||||
* @param {SVGElement} svg
|
||||
* @returns {Object}
|
||||
*/
|
||||
function getMermaidPanzoomGroup(svg) {
|
||||
if (!svg?.closest?.('.diagram-view')) return null
|
||||
@@ -73,30 +74,44 @@ function getMermaidPanzoomGroup(svg) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a saved pan/zoom transform via CSS transform.
|
||||
* @param {SVGElement} svg
|
||||
* @param {any} transform
|
||||
* @param {number} transform.x
|
||||
* @param {number} transform.y
|
||||
* @param {number} transform.scale
|
||||
* Compares two pan/zoom transforms with a small tolerance.
|
||||
* Used to avoid redundant sync work (e.g. repeated theme switches / re-renders).
|
||||
*
|
||||
* @param {Object} a - PanzoomTransform object.
|
||||
* @param {Object} b - PanzoomTransform object.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function applyPanzoomTransformToSvg(svg, transform) {
|
||||
if (!svg || !transform) return
|
||||
svg.style.transform = `scale(${transform.scale}) translate(${transform.x}px, ${transform.y}px)`
|
||||
function isSamePanzoomTransform(a, b) {
|
||||
if (!a || !b) return false
|
||||
const eps = 1e-3
|
||||
return (
|
||||
Math.abs(a.x - b.x) <= eps &&
|
||||
Math.abs(a.y - b.y) <= eps &&
|
||||
Math.abs(a.scale - b.scale) <= eps
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a saved pan/zoom transform via Panzoom instance methods.
|
||||
* @param {any} instance
|
||||
* @param {any} transform
|
||||
* @param {number} transform.x
|
||||
* @param {number} transform.y
|
||||
* @param {number} transform.scale
|
||||
* Applies a pan/zoom transform to a Panzoom instance.
|
||||
*
|
||||
* Reads the current state via `getPan()` and `getScale()` and skips if it is already
|
||||
* effectively equal to `transform` (see `isSamePanzoomTransform`).
|
||||
*
|
||||
* Note: the pan step is deferred to the next macrotask so the zoom can be applied first.
|
||||
*
|
||||
* @param {Object} instance - Panzoom instance with zoom/pan/getPan/getScale methods.
|
||||
* @param {PanzoomTransform} transform
|
||||
* @returns {void}
|
||||
*/
|
||||
function applyPanzoomTransform(instance, transform) {
|
||||
if (!instance || !transform) return
|
||||
try { instance.zoom(transform.scale, { animate: false, force: true }) } catch {}
|
||||
try { instance.pan(transform.x, transform.y, { animate: false, force: true }) } catch {}
|
||||
const currentTransform = {
|
||||
scale: instance.getScale(),
|
||||
...instance.getPan(),
|
||||
}
|
||||
if (isSamePanzoomTransform(currentTransform, transform)) return
|
||||
instance.zoom(transform.scale, { animate: false, force: true })
|
||||
setTimeout(() => instance.pan(transform.x, transform.y, { animate: false, force: true }))
|
||||
}
|
||||
{{- end }}
|
||||
|
||||
@@ -248,10 +263,6 @@ async function renderMermaidElement(el, { theme, darkMode } = {}) {
|
||||
el.setAttribute('data-processed', '')
|
||||
const svgId = svgEl?.id || id
|
||||
if (svgId) {
|
||||
{{- if $wrapped }}
|
||||
const group = getMermaidPanzoomGroup(svgEl)
|
||||
applyPanzoomTransformToSvg(svgEl, group?.transform)
|
||||
{{- end }}
|
||||
document.dispatchEvent(new CustomEvent('fixit:mermaid-rendered', { detail: { svgId } }))
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -590,6 +601,7 @@ function bindTabContainerChanged() {
|
||||
function bindThemeSync() {
|
||||
if (mermaidThemeSyncBound) return
|
||||
mermaidThemeSyncBound = true
|
||||
let lastEffectiveDark = isDarkMode()
|
||||
|
||||
const getActivePanzoomSvg = (container) => {
|
||||
const nodes = container?.querySelectorAll?.('.mermaid, .mermaid-dark')
|
||||
@@ -615,46 +627,40 @@ function bindThemeSync() {
|
||||
return null
|
||||
}
|
||||
|
||||
const resolveSourceSvg = (container) => {
|
||||
const active = getActivePanzoomSvg(container)
|
||||
const nowDark = isDarkMode()
|
||||
const fallback = nowDark
|
||||
? container.querySelector('.mermaid svg')
|
||||
: container.querySelector('.mermaid-dark svg')
|
||||
const group = active ? getMermaidPanzoomGroup(active) : null
|
||||
const last = group?.lastSvg
|
||||
if (last?.isConnected) return last
|
||||
return fallback || active
|
||||
}
|
||||
|
||||
const sync = () => {
|
||||
const sync = (nowDark) => {
|
||||
if (!mermaidPanzoomGroups) return
|
||||
document.querySelectorAll('.diagram-view .diagram-container').forEach((container) => {
|
||||
const currentActive = getActivePanzoomSvg(container)
|
||||
if (!currentActive) return
|
||||
const group = getMermaidPanzoomGroup(currentActive)
|
||||
const fallbackSource = nowDark
|
||||
? container.querySelector('.mermaid svg')
|
||||
: container.querySelector('.mermaid-dark svg')
|
||||
|
||||
const groupSeed = currentActive || fallbackSource
|
||||
if (!groupSeed) return
|
||||
const group = getMermaidPanzoomGroup(groupSeed)
|
||||
if (!group) return
|
||||
|
||||
const sourceSvg = resolveSourceSvg(container)
|
||||
const last = group?.lastSvg
|
||||
const sourceSvg = last?.isConnected ? last : (fallbackSource || currentActive)
|
||||
const sourceTransform = safeGetTransformFromSvg(sourceSvg)
|
||||
if (sourceTransform) group.transform = sourceTransform
|
||||
|
||||
if (!group.transform) return
|
||||
|
||||
const instance = panzoomInstances?.get?.(currentActive)
|
||||
if (instance) {
|
||||
if (currentActive) {
|
||||
const instance = panzoomInstances?.get?.(currentActive)
|
||||
applyPanzoomTransform(instance, group.transform)
|
||||
} else {
|
||||
applyPanzoomTransformToSvg(currentActive, group.transform)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (window.fixit?.switchThemeEventSet?.add) {
|
||||
window.fixit.switchThemeEventSet.add(async () => {
|
||||
window.fixit.switchThemeEventSet.add(async (isDark) => {
|
||||
if (isDark === lastEffectiveDark) return
|
||||
lastEffectiveDark = isDark
|
||||
await nextFrame()
|
||||
await nextFrame()
|
||||
sync()
|
||||
sync(isDark)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user