b42b1a193d
* fix: Excessive battery usage * chore: Refactor Eraser, Lasso and Laser pointer to use AnimationController * fix: Last laser trail element is not removed from SVG --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
136 lines
3.8 KiB
TypeScript
136 lines
3.8 KiB
TypeScript
import { DEFAULT_LASER_COLOR, easeOut } from "@excalidraw/common";
|
|
|
|
import type { LaserPointerOptions } from "@excalidraw/laser-pointer";
|
|
|
|
import { AnimatedTrail } from "./animatedTrail";
|
|
import { getClientColor } from "./clients";
|
|
|
|
import type { Trail } from "./animatedTrail";
|
|
import type App from "./components/App";
|
|
import type { SocketId } from "./types";
|
|
|
|
export class LaserTrails implements Trail {
|
|
public localTrail: AnimatedTrail;
|
|
private collabTrails = new Map<SocketId, AnimatedTrail>();
|
|
private container?: SVGSVGElement;
|
|
|
|
constructor(private app: App) {
|
|
this.localTrail = new AnimatedTrail(app, {
|
|
...this.getTrailOptions(),
|
|
fill: () => DEFAULT_LASER_COLOR,
|
|
});
|
|
}
|
|
|
|
private getTrailOptions() {
|
|
return {
|
|
simplify: 0,
|
|
streamline: 0.4,
|
|
sizeMapping: (c) => {
|
|
const DECAY_TIME = 1000;
|
|
const DECAY_LENGTH = 50;
|
|
const t = Math.max(
|
|
0,
|
|
1 - (performance.now() - c.pressure) / DECAY_TIME,
|
|
);
|
|
const l =
|
|
(DECAY_LENGTH -
|
|
Math.min(DECAY_LENGTH, c.totalLength - c.currentIndex)) /
|
|
DECAY_LENGTH;
|
|
|
|
return Math.min(easeOut(l), easeOut(t));
|
|
},
|
|
} as Partial<LaserPointerOptions>;
|
|
}
|
|
|
|
startPath(x: number, y: number): void {
|
|
this.localTrail.startPath(x, y);
|
|
}
|
|
|
|
addPointToPath(x: number, y: number): void {
|
|
this.localTrail.addPointToPath(x, y);
|
|
}
|
|
|
|
endPath(): void {
|
|
this.localTrail.endPath();
|
|
}
|
|
|
|
start(container: SVGSVGElement) {
|
|
this.container = container;
|
|
this.localTrail.start(container);
|
|
}
|
|
|
|
stop() {
|
|
this.localTrail.stop();
|
|
this.stopCollabTrails();
|
|
this.container = undefined;
|
|
}
|
|
|
|
private stopCollabTrails(collaborators?: App["state"]["collaborators"]) {
|
|
for (const [key, trail] of this.collabTrails) {
|
|
const collaborator = collaborators?.get(key);
|
|
|
|
if (!collaborator) {
|
|
trail.stop();
|
|
this.collabTrails.delete(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
updateCollabTrails(collaborators: App["state"]["collaborators"]) {
|
|
this.stopCollabTrails(collaborators);
|
|
|
|
if (!this.container || collaborators.size === 0) {
|
|
return;
|
|
}
|
|
|
|
for (const [key, collaborator] of collaborators.entries()) {
|
|
// Current user has their own trail drawn via localTrail
|
|
if (collaborator.isCurrentUser) {
|
|
continue;
|
|
}
|
|
|
|
// IDEA: Use the collaborator pointer coordinates to trace out the
|
|
// laser pointer trail when 1) the selected collab tool is the laser
|
|
// pointer and 2) the collab pointer button is in the "down" state.
|
|
let trail = this.collabTrails.get(key);
|
|
if (!trail) {
|
|
trail = new AnimatedTrail(this.app, {
|
|
...this.getTrailOptions(),
|
|
fill: () =>
|
|
collaborator.pointer?.laserColor ||
|
|
getClientColor(key, collaborator),
|
|
});
|
|
trail.start(this.container);
|
|
|
|
this.collabTrails.set(key, trail);
|
|
}
|
|
|
|
if (collaborator.pointer && collaborator.pointer.tool === "laser") {
|
|
const buttonDown = collaborator.button === "down";
|
|
const buttonUp = collaborator.button === "up";
|
|
const hasTrail = trail.hasCurrentTrail;
|
|
|
|
// Initialize a new trail
|
|
if (buttonDown && !hasTrail) {
|
|
trail.startPath(collaborator.pointer.x, collaborator.pointer.y);
|
|
}
|
|
|
|
// Add only original points
|
|
const lastPointOriginal = !trail.hasLastPoint(
|
|
collaborator.pointer.x,
|
|
collaborator.pointer.y,
|
|
);
|
|
if (buttonDown && lastPointOriginal) {
|
|
trail.addPointToPath(collaborator.pointer.x, collaborator.pointer.y);
|
|
}
|
|
|
|
// End the trail on button up
|
|
if (buttonUp && hasTrail) {
|
|
trail.addPointToPath(collaborator.pointer.x, collaborator.pointer.y);
|
|
trail.endPath();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|