(feature) animateToNode method

This commit is contained in:
dmx
2024-11-03 17:18:34 +04:00
parent 11fe365983
commit 9b0f572ca1

View File

@@ -115,6 +115,14 @@ export interface LGraphCanvasState {
readOnly: boolean
}
/** Easing functions for canvas animations */
const easeFunctions = {
linear: (t: number) => t,
easeInQuad: (t: number) => t * t,
easeOutQuad: (t: number) => t * (2 - t),
easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t)
}
/**
* This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.
* Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked
@@ -3291,6 +3299,71 @@ export class LGraphCanvas {
})
}
}
/**
* Centers the camera on a given node (animated version)
* @method animateToNode
**/
animateToNode(node: LGraphNode, duration: number = 350, zoom: number = 0.75, easing: string = 'easeInOutQuad') {
let animationId = null
const startTimestamp = performance.now()
const startX = this.ds.offset[0]
const startY = this.ds.offset[1]
const startScale = this.ds.scale
const cw = this.canvas.width / window.devicePixelRatio
const ch = this.canvas.height / window.devicePixelRatio
let targetScale = startScale
let targetX = startX
let targetY = startY
if (zoom > 0) {
const targetScaleX = (zoom * cw) / Math.max(node.size[0], 300)
const targetScaleY = (zoom * ch) / Math.max(node.size[1], 300)
// Choose the smaller scale to ensure the node fits into the viewport
// Ensure we don't go over the max scale
targetScale = Math.min(targetScaleX, targetScaleY, this.ds.max_scale)
targetX = -node.pos[0] - node.size[0] * 0.5 + (cw * 0.5) / targetScale
targetY = -node.pos[1] - node.size[1] * 0.5 + (ch * 0.5) / targetScale
} else {
targetX = -node.pos[0] - node.size[0] * 0.5 + (cw * 0.5) / targetScale
targetY = -node.pos[1] - node.size[1] * 0.5 + (ch * 0.5) / targetScale
}
const easeFunction = easeFunctions[easing] || easeFunctions.linear
const animate = (timestamp: number) => {
const elapsed = timestamp - startTimestamp
const progress = Math.min(elapsed / duration, 1)
const easedProgress = easeFunction(progress)
this.ds.offset[0] = startX + (targetX - startX) * easedProgress
this.ds.offset[1] = startY + (targetY - startY) * easedProgress
if (zoom > 0) {
this.ds.scale = startScale + (targetScale - startScale) * easedProgress
}
this.setDirty(true, true)
if (this.onPositionChanged) {
this.onPositionChanged({
x: this.ds.offset[0],
y: this.ds.offset[1]
})
}
if (progress < 1) {
animationId = requestAnimationFrame(animate)
} else {
cancelAnimationFrame(animationId)
}
}
animationId = requestAnimationFrame(animate)
}
/**
* adds some useful properties to a mouse event, like the position in graph coordinates
**/