Skip to content

Commit 3aafabe

Browse files
authored
events: explore using getCoalescedEvents (tldraw#5554)
Wanted to open a discussion on `getCoalescedEvents` and see if it's worth integrating into the canvas. The videos below show trying to draw with a CPU 20x slowdown. (I know we've talked about `getPredictedEvents` but that's a separate topic) Testbed: https://patrickhlauke.github.io/touch/coalesced-predicted-events/ Before: https://github.com/user-attachments/assets/f4b0648e-22e5-43a3-874a-959d6011ada7 After: https://github.com/user-attachments/assets/6a99db52-98e4-4a5c-aaab-9ccc1b970e9c ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Release notes - Improve draw fluidity on slower CPUs by using getCoalescedEvents.
1 parent 06df129 commit 3aafabe

File tree

6 files changed

+32
-8
lines changed

6 files changed

+32
-8
lines changed

packages/editor/api-report.api.md

+6
Original file line numberDiff line numberDiff line change
@@ -2807,6 +2807,10 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
28072807
transition(id: string, info?: any): this;
28082808
// (undocumented)
28092809
type: 'branch' | 'leaf' | 'root';
2810+
// (undocumented)
2811+
static useCoalescedEvents: boolean;
2812+
// (undocumented)
2813+
useCoalescedEvents: boolean;
28102814
}
28112815

28122816
// @public (undocumented)
@@ -4014,6 +4018,8 @@ export interface TLStateNodeConstructor {
40144018
initial?: string;
40154019
// (undocumented)
40164020
isLockable: boolean;
4021+
// (undocumented)
4022+
useCoalescedEvents: boolean;
40174023
}
40184024

40194025
// @public (undocumented)

packages/editor/src/lib/editor/tools/StateNode.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface TLStateNodeConstructor {
3838
initial?: string
3939
children?(): TLStateNodeConstructor[]
4040
isLockable: boolean
41+
useCoalescedEvents: boolean
4142
}
4243

4344
/** @public */
@@ -47,7 +48,8 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
4748
public editor: Editor,
4849
parent?: StateNode
4950
) {
50-
const { id, children, initial, isLockable } = this.constructor as TLStateNodeConstructor
51+
const { id, children, initial, isLockable, useCoalescedEvents } = this
52+
.constructor as TLStateNodeConstructor
5153

5254
this.id = id
5355
this._isActive = atom<boolean>('toolIsActive' + this.id, false)
@@ -83,20 +85,23 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
8385
}
8486
}
8587
this.isLockable = isLockable
88+
this.useCoalescedEvents = useCoalescedEvents
8689
this.performanceTracker = new PerformanceTracker()
8790
}
8891

8992
static id: string
9093
static initial?: string
9194
static children?: () => TLStateNodeConstructor[]
9295
static isLockable = true
96+
static useCoalescedEvents = false
9397

9498
id: string
9599
type: 'branch' | 'leaf' | 'root'
96100
shapeType?: string
97101
initial?: string
98102
children?: Record<string, StateNode>
99103
isLockable: boolean
104+
useCoalescedEvents: boolean
100105
parent: StateNode
101106

102107
/**

packages/editor/src/lib/hooks/useCanvasEvents.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useValue } from '@tldraw/state-react'
12
import React, { useMemo } from 'react'
23
import { RIGHT_MOUSE_BUTTON } from '../constants'
34
import {
@@ -11,6 +12,7 @@ import { useEditor } from './useEditor'
1112

1213
export function useCanvasEvents() {
1314
const editor = useEditor()
15+
const currentTool = useValue('current tool', () => editor.getCurrentTool(), [editor])
1416

1517
const events = useMemo(
1618
function canvasEvents() {
@@ -49,12 +51,17 @@ export function useCanvasEvents() {
4951
lastX = e.clientX
5052
lastY = e.clientY
5153

52-
editor.dispatch({
53-
type: 'pointer',
54-
target: 'canvas',
55-
name: 'pointer_move',
56-
...getPointerInfo(e),
57-
})
54+
// For tools that benefit from a higher fidelity of events,
55+
// we dispatch the coalesced events.
56+
const events = currentTool.useCoalescedEvents ? e.nativeEvent.getCoalescedEvents() : [e]
57+
for (const singleEvent of events) {
58+
editor.dispatch({
59+
type: 'pointer',
60+
target: 'canvas',
61+
name: 'pointer_move',
62+
...getPointerInfo(singleEvent),
63+
})
64+
}
5865
}
5966

6067
function onPointerUp(e: React.PointerEvent) {
@@ -159,7 +166,7 @@ export function useCanvasEvents() {
159166
onClick,
160167
}
161168
},
162-
[editor]
169+
[editor, currentTool]
163170
)
164171

165172
return events

packages/tldraw/api-report.api.md

+4
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,8 @@ export class DrawShapeTool extends StateNode {
904904
onExit(): void;
905905
// (undocumented)
906906
shapeType: string;
907+
// (undocumented)
908+
static useCoalescedEvents: boolean;
907909
}
908910

909911
// @public (undocumented)
@@ -1465,6 +1467,8 @@ export class HighlightShapeTool extends StateNode {
14651467
onExit(): void;
14661468
// (undocumented)
14671469
shapeType: string;
1470+
// (undocumented)
1471+
static useCoalescedEvents: boolean;
14681472
}
14691473

14701474
// @public (undocumented)

packages/tldraw/src/lib/shapes/draw/DrawShapeTool.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export class DrawShapeTool extends StateNode {
77
static override id = 'draw'
88
static override initial = 'idle'
99
static override isLockable = false
10+
static override useCoalescedEvents = true
1011
static override children(): TLStateNodeConstructor[] {
1112
return [Idle, Drawing]
1213
}

packages/tldraw/src/lib/shapes/highlight/HighlightShapeTool.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Idle } from '../draw/toolStates/Idle'
77
export class HighlightShapeTool extends StateNode {
88
static override id = 'highlight'
99
static override initial = 'idle'
10+
static override useCoalescedEvents = true
1011
static override children(): TLStateNodeConstructor[] {
1112
return [Idle, Drawing]
1213
}

0 commit comments

Comments
 (0)