From dccf68ddb76c9ed3f3a7c25d17639be3a365a78a Mon Sep 17 00:00:00 2001 From: Terry Jia Date: Mon, 2 Mar 2026 21:18:48 -0500 Subject: [PATCH] fix: improve painter cursor performance by bypassing Vue reactivity (#9339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Previously painter has node performance issue. Use direct DOM manipulation for cursor position updates instead of reactive refs, and add will-change-transform for GPU layer promotion. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-9339-fix-improve-painter-cursor-performance-by-bypassing-Vue-reactivity-3176d73d365081d88b23d26e774cebf5) by [Unito](https://www.unito.io) --- src/components/painter/WidgetPainter.vue | 24 +++++++++--------------- src/composables/painter/usePainter.ts | 13 ++++++------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/components/painter/WidgetPainter.vue b/src/components/painter/WidgetPainter.vue index 0c467bf3c9..5024622ca1 100644 --- a/src/components/painter/WidgetPainter.vue +++ b/src/components/painter/WidgetPainter.vue @@ -28,8 +28,9 @@ />
@@ -281,6 +282,7 @@ const { nodeId } = defineProps<{ const modelValue = defineModel({ default: '' }) const canvasEl = useTemplateRef('canvasEl') +const cursorEl = useTemplateRef('cursorEl') const controlsEl = useTemplateRef('controlsEl') const { width: controlsWidth } = useElementSize(controlsEl) const compact = computed( @@ -296,8 +298,6 @@ const { backgroundColor, canvasWidth, canvasHeight, - cursorX, - cursorY, cursorVisible, displayBrushSize, inputImageUrl, @@ -309,7 +309,7 @@ const { handlePointerLeave, handleInputImageLoad, handleClear -} = usePainter(nodeId, { canvasEl, modelValue }) +} = usePainter(nodeId, { canvasEl, cursorEl, modelValue }) const canvasContainerStyle = computed(() => ({ aspectRatio: `${canvasWidth.value} / ${canvasHeight.value}`, @@ -318,16 +318,10 @@ const canvasContainerStyle = computed(() => ({ : backgroundColor.value })) -const cursorStyle = computed(() => { - const size = displayBrushSize.value - const x = cursorX.value - size / 2 - const y = cursorY.value - size / 2 - return { - width: `${size}px`, - height: `${size}px`, - transform: `translate(${x}px, ${y}px)` - } -}) +const cursorSizeStyle = computed(() => ({ + width: `${displayBrushSize.value}px`, + height: `${displayBrushSize.value}px` +})) const brushOpacityPercent = computed({ get: () => Math.round(brushOpacity.value * 100), diff --git a/src/composables/painter/usePainter.ts b/src/composables/painter/usePainter.ts index fec324cfb2..fefb014cef 100644 --- a/src/composables/painter/usePainter.ts +++ b/src/composables/painter/usePainter.ts @@ -27,11 +27,12 @@ export const PAINTER_TOOLS: Record = { interface UsePainterOptions { canvasEl: Ref + cursorEl: Ref modelValue: Ref } export function usePainter(nodeId: string, options: UsePainterOptions) { - const { canvasEl, modelValue } = options + const { canvasEl, cursorEl, modelValue } = options const { t } = useI18n() const nodeOutputStore = useNodeOutputStore() const toastStore = useToastStore() @@ -41,8 +42,6 @@ export function usePainter(nodeId: string, options: UsePainterOptions) { const canvasWidth = ref(512) const canvasHeight = ref(512) - const cursorX = ref(0) - const cursorY = ref(0) const cursorVisible = ref(false) const inputImageUrl = ref(null) @@ -518,8 +517,10 @@ export function usePainter(nodeId: string, options: UsePainterOptions) { } function updateCursorPos(e: PointerEvent) { - cursorX.value = e.offsetX - cursorY.value = e.offsetY + const el = cursorEl.value + if (!el) return + const size = displayBrushSize.value + el.style.transform = `translate(${e.offsetX - size / 2}px, ${e.offsetY - size / 2}px)` } function handlePointerDown(e: PointerEvent) { @@ -760,8 +761,6 @@ export function usePainter(nodeId: string, options: UsePainterOptions) { canvasWidth, canvasHeight, - cursorX, - cursorY, cursorVisible, displayBrushSize,