119 lines
2.9 KiB
TypeScript
119 lines
2.9 KiB
TypeScript
(() => {
|
|
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 = {
|
|
mouseDraw: DrawEvent;
|
|
netDraw: DrawEvent;
|
|
};
|
|
|
|
const emitter = mitt<Events>();
|
|
|
|
let drawing = false;
|
|
let previous: DrawPoint | null = null;
|
|
|
|
const socket = initSocket("/draw/ws");
|
|
|
|
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]) },
|
|
};
|
|
};
|
|
|
|
emitter.on("mouseDraw", (e) => sendMessage(e));
|
|
const sendMessage = (e: DrawEvent) => socket.send(serializeDraw(e));
|
|
|
|
const mouseDown = () => {
|
|
drawing = true;
|
|
previous = null;
|
|
};
|
|
|
|
const mouseUp = () => {
|
|
drawing = false;
|
|
};
|
|
|
|
const mouseMove = (e: Pick<MouseEvent, "offsetX" | "offsetY">) => {
|
|
if (!drawing) return;
|
|
if (previous) {
|
|
emitter.emit("mouseDraw", {
|
|
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");
|
|
}
|
|
|
|
const render = (data: DrawEvent) => {
|
|
const { from, to } = data;
|
|
|
|
if (recCtx.strokeStyle != "white") {
|
|
recCtx.lineWidth = 2;
|
|
recCtx.lineCap = "round";
|
|
recCtx.strokeStyle = "white";
|
|
}
|
|
|
|
recCtx.beginPath();
|
|
recCtx.moveTo(from.x, from.y);
|
|
recCtx.lineTo(to.x, to.y);
|
|
recCtx.closePath();
|
|
recCtx.stroke();
|
|
};
|
|
|
|
emitter.on("mouseDraw", render);
|
|
emitter.on("netDraw", render);
|
|
|
|
socket.addMessageListener( (data) => {
|
|
if (data === "ping" || data.startsWith("pong")) return;
|
|
emitter.emit("netDraw", deserializeDraw(data));
|
|
});
|
|
|
|
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);
|
|
})();
|