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(import.meta.env.VITE_YORKIE_API_ADDR, {8 apiKey: import.meta.env.VITE_YORKIE_API_KEY,9});1011const doc = new yorkie.Document('simultaneous-cursors', {12 enableDevtools: true,13});1415const App = () => {16 const [clients, setClients] = useState([]);1718 const handleCursorShapeSelect = (cursorShape) => {19 doc.update((root, presence) => {20 presence.set({21 cursorShape,22 });23 });24 };2526 useEffect(() => {27 const setup = async () => {28 await client.activate();2930 doc.subscribe('presence', (event) => {31 setClients(doc.getPresences());32 });3334 await client.attach(doc, {35 initialPresence: {36 cursorShape: 'cursor',37 cursor: {38 xPos: 0,39 yPos: 0,40 },41 pointerDown: false,42 },43 });4445 window.addEventListener('beforeunload', () => {46 client.deactivate();47 });48 };4950 setup();5152 const handlePointerUp = () => {53 doc.update((root, presence) => {54 presence.set({55 pointerDown: false,56 });57 });58 };59 const handlePointerDown = () => {60 doc.update((root, presence) => {61 presence.set({62 pointerDown: true,63 });64 });65 };66 const handleMouseMove = (event) => {67 doc.update((root, presence) => {68 presence.set({69 cursor: {70 xPos: event.clientX,71 yPos: event.clientY,72 },73 });74 });75 };7677 window.addEventListener('mousedown', handlePointerDown);78 window.addEventListener('mouseup', handlePointerUp);79 window.addEventListener('mousemove', handleMouseMove);8081 return () => {82 window.removeEventListener('mousedown', handlePointerDown);83 window.removeEventListener('mouseup', handlePointerUp);84 window.removeEventListener('mousemove', handleMouseMove);85 };86 }, []);8788 return (89 <div className="general-container">90 {clients.map(91 ({ clientID, presence: { cursorShape, cursor, pointerDown } }) => {92 if (!cursor) return null;93 return (94 <Cursor95 selectedCursorShape={cursorShape}96 x={cursor.xPos}97 y={cursor.yPos}98 pointerDown={pointerDown}99 key={clientID}100 />101 );102 },103 )}104105 <CursorSelections106 handleCursorShapeSelect={handleCursorShapeSelect}107 clientsLength={clients.length}108 />109 </div>110 );111};112113export default App;
- User 1
- User 2
- User 1
- User 2
Event Log