mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-28 18:54:09 +00:00
Animate view to multiple nodes (#282)
* animateToNodes - modify existing animation method to support passing multiple nodes, in which case the view is fit to the bounding box around them * animateToBounds * amend * format * amend
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { CanvasColour, Dictionary, Direction, IBoundaryNodes, IContextMenuOptions, INodeSlot, INodeInputSlot, INodeOutputSlot, IOptionalSlotData, Point, Rect, Rect32, Size, IContextMenuValue, ISlotType, ConnectingLink, NullableProperties, Positionable, ReadOnlyPoint } from "./interfaces"
|
||||
import type { CanvasColour, Dictionary, Direction, IBoundaryNodes, IContextMenuOptions, INodeSlot, INodeInputSlot, INodeOutputSlot, IOptionalSlotData, Point, Rect, Rect32, Size, IContextMenuValue, ISlotType, ConnectingLink, NullableProperties, Positionable, ReadOnlyPoint, ReadOnlyRect } from "./interfaces"
|
||||
import type { IWidget, TWidgetValue } from "./types/widgets"
|
||||
import { LGraphNode, type NodeId } from "./LGraphNode"
|
||||
import type { CanvasDragEvent, CanvasMouseEvent, CanvasWheelEvent, CanvasEventDetail, CanvasPointerEvent } from "./types/events"
|
||||
@@ -7838,17 +7838,20 @@ export class LGraphCanvas {
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers the camera on a given node (animated version)
|
||||
* @method animateToNode
|
||||
**/
|
||||
animateToNode(
|
||||
node: LGraphNode,
|
||||
* Starts an animation to fit the view around the specified selection of nodes.
|
||||
* @param bounds The bounds to animate the view to, defined by a rectangle.
|
||||
* @param animationParameters Various parameters for the camera movement animation.
|
||||
*/
|
||||
animateToBounds(
|
||||
bounds: ReadOnlyRect,
|
||||
{
|
||||
duration = 350,
|
||||
zoom = 0.75,
|
||||
easing = EaseFunction.EASE_IN_OUT_QUAD
|
||||
}: {
|
||||
/** Duration of the animation in milliseconds. */
|
||||
duration?: number,
|
||||
/** Relative target zoom level. 1 means the view is fit exactly on the bounding box. */
|
||||
zoom?: number,
|
||||
easing?: EaseFunction
|
||||
} = {}
|
||||
@@ -7871,19 +7874,18 @@ export class LGraphCanvas {
|
||||
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)
|
||||
const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)
|
||||
const targetScaleY = (zoom * ch) / Math.max(bounds[3], 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
|
||||
}
|
||||
targetX = -bounds[0] - bounds[2] * 0.5 + (cw * 0.5) / targetScale
|
||||
targetY = -bounds[1] - bounds[3] * 0.5 + (ch * 0.5) / targetScale
|
||||
|
||||
const animate = (timestamp: number) => {
|
||||
const elapsed = timestamp - startTimestamp
|
||||
const progress = Math.min(elapsed / duration, 1)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IContextMenuValue, IPinnable, Point, Positionable, Size } from "./interfaces"
|
||||
import type { IContextMenuValue, IPinnable, Point, Positionable, ReadOnlyRect, Size } from "./interfaces"
|
||||
import type { LGraph } from "./LGraph"
|
||||
import type { ISerialisedGroup } from "./types/serialisation"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
@@ -229,6 +229,17 @@ export class LGraphGroup implements Positionable, IPinnable {
|
||||
* @param padding Value in graph units to add to all sides of the group. Default: 10
|
||||
*/
|
||||
resizeTo(objects: Iterable<Positionable>, padding: number = 10): void {
|
||||
const boundingBox = LGraphGroup.createBounds(objects, padding);
|
||||
if(boundingBox === null) return
|
||||
|
||||
this.pos[0] = boundingBox[0]
|
||||
this.pos[1] = boundingBox[1] - this.titleHeight
|
||||
this.size[0] = boundingBox[2]
|
||||
this.size[1] = boundingBox[3] + this.titleHeight
|
||||
}
|
||||
|
||||
static createBounds(objects: Iterable<Positionable>, padding: number = 10): ReadOnlyRect | null
|
||||
{
|
||||
const bounds = new Float32Array([Infinity, Infinity, -Infinity, -Infinity])
|
||||
|
||||
for (const obj of objects) {
|
||||
@@ -238,12 +249,14 @@ export class LGraphGroup implements Positionable, IPinnable {
|
||||
bounds[2] = Math.max(bounds[2], rect[0] + rect[2])
|
||||
bounds[3] = Math.max(bounds[3], rect[1] + rect[3])
|
||||
}
|
||||
if (!bounds.every(x => isFinite(x))) return
|
||||
if (!bounds.every(x => isFinite(x))) return null
|
||||
|
||||
this.pos[0] = bounds[0] - padding
|
||||
this.pos[1] = bounds[1] - padding - this.titleHeight
|
||||
this.size[0] = bounds[2] - bounds[0] + (2 * padding)
|
||||
this.size[1] = bounds[3] - bounds[1] + (2 * padding) + this.titleHeight
|
||||
return [
|
||||
bounds[0] - padding,
|
||||
bounds[1] - padding,
|
||||
bounds[2] - bounds[0] + (2 * padding),
|
||||
bounds[3] - bounds[1] + (2 * padding)
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user