User presence
Here, we add fake InstancePresence records to the store to simulate other users. If you have your own presence system, you could add real records to the store in the same way.
/* eslint-disable no-inner-declarations */
import { useRef } from 'react'
import { InstancePresenceRecordType, Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'
// There's a guide at the bottom of this file!
// [1]
const USER_NAME = 'huppy da arrow'
const MOVING_CURSOR_SPEED = 0.25 // 0 is stopped, 1 is full send
const MOVING_CURSOR_RADIUS = 100
const CURSOR_CHAT_MESSAGE = 'Hey, I think this is just great.'
// [2]
export default function UserPresenceExample() {
const rRaf = useRef<any>(-1)
return (
<div className="tldraw__editor">
<Tldraw
persistenceKey="user-presence-example"
onMount={(editor) => {
// [a]
const peerPresence = InstancePresenceRecordType.create({
id: InstancePresenceRecordType.createId(editor.store.id),
currentPageId: editor.getCurrentPageId(),
userId: 'peer-1',
userName: USER_NAME,
cursor: { x: 0, y: 0, type: 'default', rotation: 0 },
chatMessage: CURSOR_CHAT_MESSAGE,
})
editor.store.mergeRemoteChanges(() => {
editor.store.put([peerPresence])
})
// [b]
const raf = rRaf.current
cancelAnimationFrame(raf)
if (MOVING_CURSOR_SPEED > 0 || CURSOR_CHAT_MESSAGE) {
function loop() {
let cursor = peerPresence.cursor
let chatMessage = peerPresence.chatMessage
const now = Date.now()
if (MOVING_CURSOR_SPEED > 0) {
const k = 1000 / MOVING_CURSOR_SPEED
const t = (now % k) / k
cursor = {
...peerPresence.cursor,
x: 150 + Math.cos(t * Math.PI * 2) * MOVING_CURSOR_RADIUS,
y: 150 + Math.sin(t * Math.PI * 2) * MOVING_CURSOR_RADIUS,
}
}
if (CURSOR_CHAT_MESSAGE) {
const k = 1000
const t = (now % (k * 3)) / k
chatMessage =
t < 1
? ''
: t > 2
? CURSOR_CHAT_MESSAGE
: CURSOR_CHAT_MESSAGE.slice(
0,
Math.ceil((t - 1) * CURSOR_CHAT_MESSAGE.length)
)
}
editor.store.mergeRemoteChanges(() => {
editor.store.put([
{
...peerPresence,
cursor,
chatMessage,
lastActivityTimestamp: now,
},
])
})
rRaf.current = editor.timers.requestAnimationFrame(loop)
}
rRaf.current = editor.timers.requestAnimationFrame(loop)
} else {
editor.store.mergeRemoteChanges(() => {
editor.store.put([{ ...peerPresence, lastActivityTimestamp: Date.now() }])
})
rRaf.current = editor.timers.setInterval(() => {
editor.store.mergeRemoteChanges(() => {
editor.store.put([{ ...peerPresence, lastActivityTimestamp: Date.now() }])
})
}, 1000)
}
}}
/>
</div>
)
}
/*
This example shows how to add instance presence records to the store to show other users' cursors.
It is not an example of how to implement user presence, check out the yjs example for that:
https://github.com/tldraw/tldraw-yjs-example
[1]
We're going to a fake a user's cursor and chat message, these are the values we'll use.
[2]
This is where we'll render the Tldraw component. We'll use the onMount callback to access the editor
instance.
[a] For every connected peer we need to add an instance presence record to the store. We can do
this using the InstancePresenceRecordType.create function and add it to the store using the
store.put method.
[b] We'll use the requestAnimationFrame function to update the cursor position and chat message.
This is just for demonstration purposes.
*/
Is this page helpful?
Prev
Prevent shape changeNext
Code x-ray for UI events