mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-23 15:59:47 +00:00
Manage style of DOM widgets in Vue (#2946)
This commit is contained in:
@@ -23,6 +23,12 @@ export function useAbsolutePosition() {
|
||||
height: '0px'
|
||||
})
|
||||
|
||||
/**
|
||||
* Update the position of the element on the litegraph canvas.
|
||||
*
|
||||
* @param config
|
||||
* @param extraStyle
|
||||
*/
|
||||
const updatePosition = (
|
||||
config: PositionConfig,
|
||||
extraStyle?: CSSProperties
|
||||
@@ -41,8 +47,36 @@ export function useAbsolutePosition() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the position and size of the element on the litegraph canvas,
|
||||
* with CSS transform scaling applied.
|
||||
*
|
||||
* @param config
|
||||
* @param extraStyle
|
||||
*/
|
||||
const updatePositionWithTransform = (
|
||||
config: PositionConfig,
|
||||
extraStyle?: CSSProperties
|
||||
) => {
|
||||
const { pos, size, scale = canvasStore.canvas?.ds?.scale ?? 1 } = config
|
||||
const [left, top] = app.canvasPosToClientPos(pos)
|
||||
const [width, height] = size
|
||||
|
||||
style.value = {
|
||||
...style.value,
|
||||
transformOrigin: '0 0',
|
||||
transform: `scale(${scale})`,
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
...extraStyle
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
style,
|
||||
updatePosition
|
||||
updatePosition,
|
||||
updatePositionWithTransform
|
||||
}
|
||||
}
|
||||
|
||||
123
src/composables/element/useDomClipping.ts
Normal file
123
src/composables/element/useDomClipping.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { CSSProperties, ref } from 'vue'
|
||||
|
||||
interface Rect {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the intersection between two rectangles
|
||||
*/
|
||||
function intersect(a: Rect, b: Rect): [number, number, number, number] | null {
|
||||
const x1 = Math.max(a.x, b.x)
|
||||
const y1 = Math.max(a.y, b.y)
|
||||
const x2 = Math.min(a.x + a.width, b.x + b.width)
|
||||
const y2 = Math.min(a.y + a.height, b.y + b.height)
|
||||
|
||||
if (x1 >= x2 || y1 >= y2) {
|
||||
return null
|
||||
}
|
||||
|
||||
return [x1, y1, x2 - x1, y2 - y1]
|
||||
}
|
||||
|
||||
export interface ClippingOptions {
|
||||
margin?: number
|
||||
}
|
||||
|
||||
export const useDomClipping = (options: ClippingOptions = {}) => {
|
||||
const style = ref<CSSProperties>({})
|
||||
const { margin = 4 } = options
|
||||
|
||||
/**
|
||||
* Calculates a clip path for an element based on its intersection with a selected area
|
||||
*/
|
||||
const calculateClipPath = (
|
||||
elementRect: DOMRect,
|
||||
canvasRect: DOMRect,
|
||||
isSelected: boolean,
|
||||
selectedArea?: {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
scale: number
|
||||
offset: [number, number]
|
||||
}
|
||||
): string => {
|
||||
if (!isSelected && selectedArea) {
|
||||
const { scale, offset } = selectedArea
|
||||
|
||||
// Get intersection in browser space
|
||||
const intersection = intersect(
|
||||
{
|
||||
x: elementRect.left - canvasRect.left,
|
||||
y: elementRect.top - canvasRect.top,
|
||||
width: elementRect.width,
|
||||
height: elementRect.height
|
||||
},
|
||||
{
|
||||
x: (selectedArea.x + offset[0] - margin) * scale,
|
||||
y: (selectedArea.y + offset[1] - margin) * scale,
|
||||
width: (selectedArea.width + 2 * margin) * scale,
|
||||
height: (selectedArea.height + 2 * margin) * scale
|
||||
}
|
||||
)
|
||||
|
||||
if (!intersection) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// Convert intersection to canvas scale (element has scale transform)
|
||||
const clipX =
|
||||
(intersection[0] - elementRect.left + canvasRect.left) / scale + 'px'
|
||||
const clipY =
|
||||
(intersection[1] - elementRect.top + canvasRect.top) / scale + 'px'
|
||||
const clipWidth = intersection[2] / scale + 'px'
|
||||
const clipHeight = intersection[3] / scale + 'px'
|
||||
|
||||
return `polygon(0% 0%, 0% 100%, ${clipX} 100%, ${clipX} ${clipY}, calc(${clipX} + ${clipWidth}) ${clipY}, calc(${clipX} + ${clipWidth}) calc(${clipY} + ${clipHeight}), ${clipX} calc(${clipY} + ${clipHeight}), ${clipX} 100%, 100% 100%, 100% 0%)`
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the clip-path style based on element and selection information
|
||||
*/
|
||||
const updateClipPath = (
|
||||
element: HTMLElement,
|
||||
canvasElement: HTMLCanvasElement,
|
||||
isSelected: boolean,
|
||||
selectedArea?: {
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
scale: number
|
||||
offset: [number, number]
|
||||
}
|
||||
) => {
|
||||
const elementRect = element.getBoundingClientRect()
|
||||
const canvasRect = canvasElement.getBoundingClientRect()
|
||||
|
||||
const clipPath = calculateClipPath(
|
||||
elementRect,
|
||||
canvasRect,
|
||||
isSelected,
|
||||
selectedArea
|
||||
)
|
||||
|
||||
style.value = {
|
||||
clipPath: clipPath || 'none',
|
||||
willChange: 'clip-path'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
style,
|
||||
updateClipPath
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user