mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 14:54:37 +00:00
229 lines
7.4 KiB
TypeScript
229 lines
7.4 KiB
TypeScript
import type { Point, Rect, Rect32 } from "./interfaces"
|
|
import type { CanvasMouseEvent } from "./types/events"
|
|
import { LiteGraph } from "./litegraph"
|
|
|
|
export class DragAndScale {
|
|
/** Maximum scale (zoom in) */
|
|
max_scale: number
|
|
/** Minimum scale (zoom out) */
|
|
min_scale: number
|
|
offset: Point
|
|
scale: number
|
|
enabled: boolean
|
|
last_mouse: Point
|
|
element?: HTMLCanvasElement
|
|
visible_area: Rect32
|
|
_binded_mouse_callback
|
|
dragging?: boolean
|
|
viewport?: Rect
|
|
|
|
onredraw?(das: DragAndScale): void
|
|
/** @deprecated */
|
|
onmouse?(e: unknown): boolean
|
|
|
|
constructor(element?: HTMLCanvasElement, skip_events?: boolean) {
|
|
this.offset = new Float32Array([0, 0])
|
|
this.scale = 1
|
|
this.max_scale = 10
|
|
this.min_scale = 0.1
|
|
this.onredraw = null
|
|
this.enabled = true
|
|
this.last_mouse = [0, 0]
|
|
this.element = null
|
|
this.visible_area = new Float32Array(4)
|
|
|
|
if (element) {
|
|
this.element = element
|
|
if (!skip_events) {
|
|
this.bindEvents(element)
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @deprecated Has not been kept up to date */
|
|
bindEvents(element: Node): void {
|
|
this.last_mouse = new Float32Array(2)
|
|
|
|
this._binded_mouse_callback = this.onMouse.bind(this)
|
|
|
|
LiteGraph.pointerListenerAdd(element, "down", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerAdd(element, "move", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerAdd(element, "up", this._binded_mouse_callback)
|
|
|
|
element.addEventListener(
|
|
"mousewheel",
|
|
this._binded_mouse_callback,
|
|
false
|
|
)
|
|
element.addEventListener("wheel", this._binded_mouse_callback, false)
|
|
}
|
|
|
|
computeVisibleArea(viewport: Rect): void {
|
|
if (!this.element) {
|
|
this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0
|
|
return
|
|
}
|
|
let width = this.element.width
|
|
let height = this.element.height
|
|
let startx = -this.offset[0]
|
|
let starty = -this.offset[1]
|
|
if (viewport) {
|
|
startx += viewport[0] / this.scale
|
|
starty += viewport[1] / this.scale
|
|
width = viewport[2]
|
|
height = viewport[3]
|
|
}
|
|
const endx = startx + width / this.scale
|
|
const endy = starty + height / this.scale
|
|
this.visible_area[0] = startx
|
|
this.visible_area[1] = starty
|
|
this.visible_area[2] = endx - startx
|
|
this.visible_area[3] = endy - starty
|
|
}
|
|
|
|
/** @deprecated Has not been kept up to date */
|
|
onMouse(e: CanvasMouseEvent) {
|
|
if (!this.enabled) {
|
|
return
|
|
}
|
|
|
|
const canvas = this.element
|
|
const rect = canvas.getBoundingClientRect()
|
|
const x = e.clientX - rect.left
|
|
const y = e.clientY - rect.top
|
|
// FIXME: "canvasx" / y are not referenced anywhere - wrong case
|
|
// @ts-expect-error Incorrect case
|
|
e.canvasx = x
|
|
// @ts-expect-error Incorrect case
|
|
e.canvasy = y
|
|
e.dragging = this.dragging
|
|
|
|
const is_inside = !this.viewport || (this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]))
|
|
|
|
let ignore = false
|
|
if (this.onmouse) {
|
|
ignore = this.onmouse(e)
|
|
}
|
|
|
|
if (e.type == LiteGraph.pointerevents_method + "down" && is_inside) {
|
|
this.dragging = true
|
|
LiteGraph.pointerListenerRemove(canvas, "move", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerAdd(document, "move", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerAdd(document, "up", this._binded_mouse_callback)
|
|
} else if (e.type == LiteGraph.pointerevents_method + "move") {
|
|
if (!ignore) {
|
|
const deltax = x - this.last_mouse[0]
|
|
const deltay = y - this.last_mouse[1]
|
|
if (this.dragging) {
|
|
this.mouseDrag(deltax, deltay)
|
|
}
|
|
}
|
|
} else if (e.type == LiteGraph.pointerevents_method + "up") {
|
|
this.dragging = false
|
|
LiteGraph.pointerListenerRemove(document, "move", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerRemove(document, "up", this._binded_mouse_callback)
|
|
LiteGraph.pointerListenerAdd(canvas, "move", this._binded_mouse_callback)
|
|
} else if (is_inside &&
|
|
(e.type == "mousewheel" ||
|
|
e.type == "wheel" ||
|
|
e.type == "DOMMouseScroll")) {
|
|
// @ts-expect-error Deprecated
|
|
e.eventType = "mousewheel"
|
|
// @ts-expect-error Deprecated
|
|
if (e.type == "wheel") e.wheel = -e.deltaY
|
|
// @ts-expect-error Deprecated
|
|
else e.wheel = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60
|
|
|
|
//from stack overflow
|
|
// @ts-expect-error Deprecated
|
|
e.delta = e.wheelDelta
|
|
// @ts-expect-error Deprecated
|
|
? e.wheelDelta / 40
|
|
: e.deltaY
|
|
? -e.deltaY / 3
|
|
: 0
|
|
// @ts-expect-error Deprecated
|
|
this.changeDeltaScale(1.0 + e.delta * 0.05)
|
|
}
|
|
|
|
this.last_mouse[0] = x
|
|
this.last_mouse[1] = y
|
|
|
|
if (is_inside) {
|
|
e.preventDefault()
|
|
e.stopPropagation()
|
|
return false
|
|
}
|
|
}
|
|
|
|
toCanvasContext(ctx: CanvasRenderingContext2D): void {
|
|
ctx.scale(this.scale, this.scale)
|
|
ctx.translate(this.offset[0], this.offset[1])
|
|
}
|
|
|
|
convertOffsetToCanvas(pos: Point): Point {
|
|
return [
|
|
(pos[0] + this.offset[0]) * this.scale,
|
|
(pos[1] + this.offset[1]) * this.scale
|
|
]
|
|
}
|
|
|
|
convertCanvasToOffset(pos: Point, out?: Point): Point {
|
|
out = out || [0, 0]
|
|
out[0] = pos[0] / this.scale - this.offset[0]
|
|
out[1] = pos[1] / this.scale - this.offset[1]
|
|
return out
|
|
}
|
|
|
|
/** @deprecated Has not been kept up to date */
|
|
mouseDrag(x: number, y: number): void {
|
|
this.offset[0] += x / this.scale
|
|
this.offset[1] += y / this.scale
|
|
|
|
this.onredraw?.(this)
|
|
}
|
|
|
|
changeScale(value: number, zooming_center?: Point): void {
|
|
if (value < this.min_scale) {
|
|
value = this.min_scale
|
|
} else if (value > this.max_scale) {
|
|
value = this.max_scale
|
|
}
|
|
|
|
if (value == this.scale) return
|
|
if (!this.element) return
|
|
|
|
const rect = this.element.getBoundingClientRect()
|
|
if (!rect) return
|
|
|
|
zooming_center = zooming_center || [
|
|
rect.width * 0.5,
|
|
rect.height * 0.5
|
|
]
|
|
const center = this.convertCanvasToOffset(zooming_center)
|
|
this.scale = value
|
|
if (Math.abs(this.scale - 1) < 0.01) this.scale = 1
|
|
|
|
const new_center = this.convertCanvasToOffset(zooming_center)
|
|
const delta_offset = [
|
|
new_center[0] - center[0],
|
|
new_center[1] - center[1]
|
|
]
|
|
|
|
this.offset[0] += delta_offset[0]
|
|
this.offset[1] += delta_offset[1]
|
|
|
|
this.onredraw?.(this)
|
|
}
|
|
|
|
changeDeltaScale(value: number, zooming_center?: Point): void {
|
|
this.changeScale(this.scale * value, zooming_center)
|
|
}
|
|
|
|
reset(): void {
|
|
this.scale = 1
|
|
this.offset[0] = 0
|
|
this.offset[1] = 0
|
|
}
|
|
}
|