Skip to content

Commit ec8ee49

Browse files
authored
[Bug] Fix zoom lag of DOM widget (#3714)
1 parent 5337211 commit ec8ee49

File tree

4 files changed

+48
-23
lines changed

4 files changed

+48
-23
lines changed

browser_tests/tests/loadWorkflowInMedia.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ test.describe('Load Workflow in Media', () => {
99
'no_workflow.webp',
1010
'large_workflow.webp',
1111
'workflow.webm',
12-
'workflow.glb',
12+
// Skipped due to 3d widget unstable visual result.
13+
// 3d widget shows grid after fully loaded.
14+
// 'workflow.glb',
1315
'workflow.mp4',
1416
'workflow.mov',
1517
'workflow.m4v',

browser_tests/tests/widget.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ test.describe('Image widget', () => {
187187
})
188188

189189
test.describe('Animated image widget', () => {
190-
test('Shows preview of uploaded animated image', async ({ comfyPage }) => {
190+
// https://github.com/Comfy-Org/ComfyUI_frontend/issues/3718
191+
test.skip('Shows preview of uploaded animated image', async ({
192+
comfyPage
193+
}) => {
191194
await comfyPage.loadWorkflow('widgets/load_animated_webp')
192195

193196
// Get position of the load animated webp node

src/components/graph/widgets/DomWidget.vue

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</template>
1818

1919
<script setup lang="ts">
20-
import { useEventListener } from '@vueuse/core'
20+
import { useElementBounding, useEventListener } from '@vueuse/core'
2121
import { CSSProperties, computed, onMounted, ref, watch } from 'vue'
2222
2323
import { useAbsolutePosition } from '@/composables/element/useAbsolutePosition'
@@ -38,18 +38,16 @@ const emit = defineEmits<{
3838
3939
const widgetElement = ref<HTMLElement | undefined>()
4040
41+
/**
42+
* @note Do NOT convert style to a computed value, as it will cause lag when
43+
* updating the style on different animation frames. Vue's computed value is
44+
* evaluated asynchronously.
45+
*/
46+
const style = ref<CSSProperties>({})
4147
const { style: positionStyle, updatePosition } = useAbsolutePosition({
4248
useTransform: true
4349
})
4450
const { style: clippingStyle, updateClipPath } = useDomClipping()
45-
const style = computed<CSSProperties>(() => ({
46-
...positionStyle.value,
47-
...(enableDomClipping.value ? clippingStyle.value : {}),
48-
zIndex: widgetState.zIndex,
49-
pointerEvents:
50-
widgetState.readonly || widget.computedDisabled ? 'none' : 'auto',
51-
opacity: widget.computedDisabled ? 0.5 : 1
52-
}))
5351
5452
const canvasStore = useCanvasStore()
5553
const settingStore = useSettingStore()
@@ -88,13 +86,28 @@ const updateDomClipping = () => {
8886
)
8987
}
9088
89+
/**
90+
* @note mapping between canvas position and client position depends on the
91+
* canvas element's position, so we need to watch the canvas element's position
92+
* and update the position of the widget accordingly.
93+
*/
94+
const { left, top } = useElementBounding(canvasStore.getCanvas().canvas)
9195
watch(
92-
() => widgetState,
93-
(newState) => {
94-
updatePosition(newState)
96+
[() => widgetState, left, top],
97+
([widgetState, _, __]) => {
98+
updatePosition(widgetState)
9599
if (enableDomClipping.value) {
96100
updateDomClipping()
97101
}
102+
103+
style.value = {
104+
...positionStyle.value,
105+
...(enableDomClipping.value ? clippingStyle.value : {}),
106+
zIndex: widgetState.zIndex,
107+
pointerEvents:
108+
widgetState.readonly || widget.computedDisabled ? 'none' : 'auto',
109+
opacity: widget.computedDisabled ? 0.5 : 1
110+
}
98111
},
99112
{ deep: true }
100113
)

src/composables/element/useAbsolutePosition.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Size, Vector2 } from '@comfyorg/litegraph'
2-
import { CSSProperties, computed, ref } from 'vue'
2+
import { CSSProperties, ref } from 'vue'
33

44
import { useCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
55
import { useCanvasStore } from '@/stores/graphStore'
@@ -23,13 +23,20 @@ export function useAbsolutePosition(options: { useTransform?: boolean } = {}) {
2323
lgCanvas
2424
)
2525

26-
const position = ref<PositionConfig>({
27-
pos: [0, 0],
28-
size: [0, 0]
29-
})
26+
/**
27+
* @note Do NOT convert style to a computed value, as it will cause lag when
28+
* updating the style on different animation frames. Vue's computed value is
29+
* evaluated asynchronously.
30+
*/
31+
const style = ref<CSSProperties>({})
3032

31-
const style = computed<CSSProperties>(() => {
32-
const { pos, size, scale = lgCanvas.ds.scale } = position.value
33+
/**
34+
* Compute the style of the element based on the position and size.
35+
*
36+
* @param position
37+
*/
38+
const computeStyle = (position: PositionConfig): CSSProperties => {
39+
const { pos, size, scale = lgCanvas.ds.scale } = position
3340
const [left, top] = canvasPosToClientPos(pos)
3441
const [width, height] = size
3542

@@ -50,15 +57,15 @@ export function useAbsolutePosition(options: { useTransform?: boolean } = {}) {
5057
width: `${width * scale}px`,
5158
height: `${height * scale}px`
5259
}
53-
})
60+
}
5461

5562
/**
5663
* Update the position of the element on the litegraph canvas.
5764
*
5865
* @param config
5966
*/
6067
const updatePosition = (config: PositionConfig) => {
61-
position.value = config
68+
style.value = computeStyle(config)
6269
}
6370

6471
return {

0 commit comments

Comments
 (0)