Simultaneous Cursors
This demo shows the real-time collaborative version of simple drawing, cursor animation with Yorkie and React.
App.jsx
1import { useEffect, useState } from 'react';2import yorkie from '@yorkie-js/sdk';3import Cursor from './components/Cursor';4import CursorSelections from './components/CursorSelections';5import './App.css';67const client = new yorkie.Client({8 rpcAddr: import.meta.env.VITE_YORKIE_API_ADDR,9 apiKey: import.meta.env.VITE_YORKIE_API_KEY,10});1112const doc = new yorkie.Document('simultaneous-cursors', {13 enableDevtools: true,14});1516const App = () => {17 const [clients, setClients] = useState([]);1819 const handleCursorShapeSelect = (cursorShape) => {20 doc.update((root, presence) => {21 presence.set({22 cursorShape,23 });24 });25 };2627 useEffect(() => {28 const setup = async () => {29 await client.activate();3031 doc.subscribe('presence', (event) => {32 setClients(doc.getPresences());33 });3435 await client.attach(doc, {36 initialPresence: {37 cursorShape: 'cursor',38 cursor: {39 xPos: 0,40 yPos: 0,41 },42 pointerDown: false,43 },44 });45 };4647 setup();4849 const handlePointerUp = () => {50 doc.update((root, presence) => {51 presence.set({52 pointerDown: false,53 });54 });55 };56 const handlePointerDown = () => {57 doc.update((root, presence) => {58 presence.set({59 pointerDown: true,60 });61 });62 };63 const handleMouseMove = (event) => {64 doc.update((root, presence) => {65 presence.set({66 cursor: {67 xPos: event.clientX,68 yPos: event.clientY,69 },70 });71 });72 };7374 window.addEventListener('mousedown', handlePointerDown);75 window.addEventListener('mouseup', handlePointerUp);76 window.addEventListener('mousemove', handleMouseMove);7778 return () => {79 window.removeEventListener('mousedown', handlePointerDown);80 window.removeEventListener('mouseup', handlePointerUp);81 window.removeEventListener('mousemove', handleMouseMove);82 };83 }, []);8485 return (86 <div className="general-container">87 {clients.map(88 ({ clientID, presence: { cursorShape, cursor, pointerDown } }) => {89 if (!cursor) return null;90 return (91 <Cursor92 selectedCursorShape={cursorShape}93 x={cursor.xPos}94 y={cursor.yPos}95 pointerDown={pointerDown}96 key={clientID}97 />98 );99 },100 )}101102 <CursorSelections103 handleCursorShapeSelect={handleCursorShapeSelect}104 clientsLength={clients.length}105 />106 </div>107 );108};109110export default App;
- User 1
- User 2
- User 1
- User 2
Event Log