(() => { const drawElement = document.getElementById( "drawDiv" ) as HTMLDivElement | null; if (!drawElement) { throw new Error("canvas not found"); } type DrawPoint = { x: number; y: number; }; type DrawEvent = { from: DrawPoint; to: DrawPoint; }; type Events = { draw: DrawEvent; }; const drawEmitter = mitt(); let drawing = false; const socketUrl = "/draw/ws"; let socket = new WebSocket(socketUrl); socket.onerror = console.error; socket.onclose = console.warn; const serializeDraw = ({from, to }: DrawEvent) => `${from.x},${from.y},${to.x},${to.y}`; const deserializeDraw = (message: string): DrawEvent => { const parts = message.split(","); return { from: { x: Number(parts[0]), y: Number(parts[1]), }, to: { x: Number(parts[2]), y: Number(parts[3]), }, }; }; drawEmitter.on("draw", (e) => sendMessage(e)); const sendMessage = (e: DrawEvent) => { if (socket.readyState !== socket.OPEN) { socket = new WebSocket(socketUrl); } socket.send(serializeDraw(e)); }; const mouseDown = () => { drawing = true; previous=null }; const mouseUp = () => { drawing = false; }; let previous: DrawPoint | null = null; const mouseMove = (e: Pick) => { if (!drawing) return; if (previous) { drawEmitter.emit("draw", { from: previous, to: { x: e.offsetX, y: e.offsetY }, }); } previous = { x: e.offsetX, y: e.offsetY }; }; const touchMove = (e: TouchEvent) => { e.preventDefault(); mouseMove({ offsetX: e.touches[0].clientX - drawElement.offsetLeft, offsetY: e.touches[0].clientY - drawElement.offsetTop, }); }; drawElement.addEventListener("mousedown", mouseDown); drawElement.addEventListener("mouseup", mouseUp); drawElement.addEventListener("mousemove", mouseMove); drawElement.addEventListener("touchstart", mouseDown); drawElement.addEventListener("touchend", mouseUp); drawElement.addEventListener("touchmove", touchMove); const receiveCanvas = document.getElementById( "receiveCanvas" ) as HTMLCanvasElement | null; const recCtx = receiveCanvas?.getContext("2d"); if (!recCtx || !receiveCanvas) { throw new Error("canvas not found"); } //@ts-expect-error window.ctx = recCtx; setTimeout(()=>{ recCtx.lineWidth = 2; recCtx.lineCap = "round"; recCtx.strokeStyle = "white"; },10) socket.addEventListener("message", (event) => { if(event.data==="ping" || event.data.startsWith('pong')) return const { from, to } = deserializeDraw(event.data); recCtx.beginPath(); recCtx.moveTo(from.x, from.y); recCtx.lineTo(to.x, to.y); recCtx.closePath(); recCtx.stroke(); console.log(from, to); }); setInterval(() => { if (socket.readyState !== socket.OPEN) { socket = new WebSocket(socketUrl); } socket.send("ping"); }, 10000); const resizeObserver = new ResizeObserver((entries) => { const entry = entries.at(0); receiveCanvas.height = entry?.contentRect.height ?? 500; receiveCanvas.width = entry?.contentRect.width ?? 500; }); resizeObserver.observe(drawElement); })();