Newer
Older
import { line, curveLinear } from "d3-shape"
import {
canvas,
connectedPeers,
peerButton,
peerIDElem,
userIDElem,
penButton,
eraserButton,
} from "./elements.js"
import { connect } from "./room.js"
// TODO: switch to curve interpolation that respects mouse points based on velocity
const lineFn = line()
.x((d) => d[0])
.y((d) => d[1])
.curve(curveLinear)
const tools = {
PEN: "pen",
ERASER: "eraser",
}
const STROKECOLOUR = "blue"
const STROKERADIUS = 2
const ERASERRADIUS = STROKERADIUS * 2
const pathElems = new Map()
const addOrUpdatePathElem = (id, points) => {
let pathElem = pathElems.get(id)
if (pathElem == null) {
pathElem = document.createElementNS("http://www.w3.org/2000/svg", "g")
pathElem.setAttribute("stroke", STROKECOLOUR)
pathElem.setAttribute("stroke-width", STROKERADIUS * 2)
pathElem.setAttribute("fill", "none")
pathElem.setAttribute("pointer-events", "none")
pathElem.setAttribute("marker-start", "url(#dot)")
pathElem.setAttribute("marker-end", "url(#dot)")
canvas.appendChild(pathElem)
pathElems.set(id, pathElem)
Moritz Langenstein
committed
pathElem.innerHTML = ""
// Push a fake path split to generate the last path
points.push([-1, -1, false])
Moritz Langenstein
committed
let subpath = []
Moritz Langenstein
committed
for (let point of points) {
if (point[0] === undefined) {
continue
Moritz Langenstein
committed
}
if (point[2] === false) {
if (subpath.length === 1) {
let subpathElem = document.createElementNS(
"http://www.w3.org/2000/svg",
"circle",
)
subpathElem.setAttribute("stroke", "none")
subpathElem.setAttribute("fill", STROKECOLOUR)
subpathElem.setAttribute("cx", subpath[0][0])
subpathElem.setAttribute("cy", subpath[0][1])
subpathElem.setAttribute("r", STROKERADIUS)
pathElem.appendChild(subpathElem)
} else if (subpath.length > 0) {
let subpathElem = document.createElementNS(
"http://www.w3.org/2000/svg",
"path",
)
subpathElem.setAttribute("d", lineFn(subpath))
pathElem.appendChild(subpathElem)
}
Moritz Langenstein
committed
subpath = []
Moritz Langenstein
committed
continue
Moritz Langenstein
committed
}
subpath.push(point)
Moritz Langenstein
committed
}
const getDistance = (a, b) => {
return Math.sqrt(
(a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]),
)
}
let userInput = false
let currentTool = tools.PEN
Moritz Langenstein
committed
userIDElem.value = room.ownID || ""
room.addEventListener("allocateOwnID", ({ detail: id }) => {
userIDElem.value = id
})
Moritz Langenstein
committed
room.addEventListener("userJoin", ({ detail: id }) => {
const peerElem = document.createElement("li")
peerElem.innerHTML = id
connectedPeers.appendChild(peerElem)
})
room.addEventListener("userLeave", ({ detail: id }) => {
for (const peerElem of connectedPeers.children) {
if (peerElem.innerHTML == id) {
connectedPeers.removeChild(peerElem)
room.addEventListener("addOrUpdatePath", ({ detail: { id, points } }) => {
addOrUpdatePathElem(id, points)
})
userInput = true
Moritz Langenstein
committed
let mouse = [e.offsetX, e.offsetY]
Moritz Langenstein
committed
if (currentTool === tools.PEN) {
currentPathID = room.addPath(mouse)
} else if (currentTool === tools.ERASER) {
room.getPaths().forEach((points, pathID) => {
points.forEach((point, i) => {
if (getDistance(mouse, point) <= ERASERRADIUS) {
room.erasePoint(pathID, i)
})
})
Moritz Langenstein
committed
}
Moritz Langenstein
committed
canvas.addEventListener("mouseleave", () => {
userInput = false
Moritz Langenstein
committed
userInput = false
Moritz Langenstein
committed
if (!userInput) {
return
}
let mouse = [e.offsetX, e.offsetY]
if (currentTool === tools.PEN) {
room.extendPath(currentPathID, mouse)
} else if (currentTool === tools.ERASER) {
room.getPaths().forEach((points, pathID) => {
points.forEach((point, i) => {
if (getDistance(mouse, point) <= ERASERRADIUS) {
room.erasePoint(pathID, i)
Moritz Langenstein
committed
}
peerButton.addEventListener("click", () => {
const peerID = peerIDElem.value
if (peerID == "") {
return
Moritz Langenstein
committed
}
room.inviteUser(peerID)
peerIDElem.value = ""
Moritz Langenstein
committed
})
penButton.addEventListener("click", () => {
currentTool = tools.PEN
penButton.classList.add("selected")
eraserButton.classList.remove("selected")
})
eraserButton.addEventListener("click", () => {
currentTool = tools.ERASER
penButton.classList.remove("selected")
eraserButton.classList.add("selected")
Moritz Langenstein
committed
})