Loading packages/client/src/components/CanvasWrapper.tsx +53 −12 Original line number Diff line number Diff line Loading @@ -33,10 +33,6 @@ const Cursor = () => { const { cursor } = useAppContext(); const [color, setColor] = useState<string>(); useEffect(() => { console.log("color", color); }, [color]); useEffect(() => { if (typeof cursor.color === "number") { const color = Canvas.instance?.Pallete.getColor(cursor.color); Loading Loading @@ -71,17 +67,19 @@ const CanvasInner = () => { useAppContext(); const PanZoom = useContext(RendererContext); const handlePixelWhois = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { /** * Is the canvas coordinate within the bounds of the canvas? */ const isCoordInCanvas = useCallback( (x: number, y: number): boolean => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePixelWhois] canvas instance does not exist" "[CanvasWrapper#isCoordInCanvas] canvas instance does not exist" ); return; return false; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (x < 0 || y < 0) return; // discard if out of bounds if (x < 0 || y < 0) return false; // not positive, impossible to be on canvas // canvas size can dynamically change, so we need to check the current config // we're depending on canvas.instance's config so we don't have to use a react dependency Loading @@ -92,14 +90,31 @@ const CanvasInner = () => { }, } = canvas.current.getConfig(); if (x >= width || y >= height) return; // out of bounds if (x >= width || y >= height) return false; // out of bounds } else { // although this should never happen, log it console.warn( "[CanvasWrapper#handlePixelWhois] canvas config is not available yet" "[CanvasWrapper#isCoordInCanvas] canvas config is not available yet" ); } return true; }, [canvas.current] ); const handlePixelWhois = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePixelWhois] canvas instance does not exist" ); return; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (!isCoordInCanvas(x, y)) return; // out of bounds // ....... // ....... // ....... Loading @@ -114,6 +129,30 @@ const CanvasInner = () => { [canvas.current] ); const handlePickPixel = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePickPixel] canvas instance does not exist" ); return; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (!isCoordInCanvas(x, y)) return; // out of bounds const pixel = canvas.current.getPixel(x, y); if (!pixel) return; // no need to use canvas#setCursor as Palette.tsx already does that setCursor((v) => ({ ...v, color: pixel.color, })); }, [canvas.current] ); useEffect(() => { if (!canvasRef.current) return; canvas.current = new Canvas(canvasRef.current!, PanZoom); Loading @@ -126,9 +165,11 @@ const CanvasInner = () => { }); KeybindManager.on("PIXEL_WHOIS", handlePixelWhois); KeybindManager.on("PICK_COLOR", handlePickPixel); return () => { KeybindManager.off("PIXEL_WHOIS", handlePixelWhois); KeybindManager.off("PICK_COLOR", handlePickPixel); canvas.current!.destroy(); }; }, [PanZoom]); Loading packages/client/src/components/Toolbar/Palette.tsx +8 −15 Original line number Diff line number Diff line Loading @@ -3,21 +3,14 @@ import { useAppContext } from "../../contexts/AppContext"; import { Canvas } from "../../lib/canvas"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { IPaletteContext } from "@sc07-canvas/lib/src/net"; import { KeybindManager } from "../../lib/keybinds"; export const Palette = () => { const { config, user, setCursor } = useAppContext<true>(); const [pallete, setPallete] = useState<IPaletteContext>({}); const { config, user, cursor, setCursor } = useAppContext<true>(); useEffect(() => { Canvas.instance?.updatePallete(pallete); setCursor((v) => ({ ...v, color: pallete.color, })); }, [pallete]); Canvas.instance?.updateCursor(cursor.color); }, [cursor]); useEffect(() => { const handleDeselect = () => { Loading @@ -42,8 +35,8 @@ export const Palette = () => { className="pallete-color--deselect" title="Deselect Color" onClick={() => { setPallete(({ color, ...pallete }) => { return pallete; setCursor(({ color, ...cursor }) => { return cursor; }); }} > Loading @@ -53,7 +46,7 @@ export const Palette = () => { <button key={color.id} aria-label={color.name} className={["pallete-color", color.id === pallete.color && "active"] className={["pallete-color", color.id === cursor.color && "active"] .filter((a) => a) .join(" ")} style={{ Loading @@ -61,9 +54,9 @@ export const Palette = () => { }} title={color.name} onClick={() => { setPallete((pallete) => { setCursor((cursor) => { return { ...pallete, ...cursor, color: color.id, }; }); Loading packages/client/src/lib/canvas.ts +16 −12 Original line number Diff line number Diff line import EventEmitter from "eventemitter3"; import { ClientConfig, IPaletteContext, IPosition, Pixel, } from "@sc07-canvas/lib/src/net"; import { ClientConfig, IPosition, Pixel } from "@sc07-canvas/lib/src/net"; import Network from "./network"; import { ClickEvent, Loading Loading @@ -34,7 +29,7 @@ export class Canvas extends EventEmitter<CanvasEvents> { private canvas: HTMLCanvasElement; private PanZoom: PanZoom; private cursor = { x: -1, y: -1 }; private cursor: { x: number; y: number; color?: number } = { x: -1, y: -1 }; private pixels: { [x_y: string]: { color: number; type: "full" | "pending" }; } = {}; Loading Loading @@ -174,6 +169,10 @@ export class Canvas extends EventEmitter<CanvasEvents> { return pixels; } getPixel(x: number, y: number): { color: number } | undefined { return this.pixels[x + "_" + y]; } handleLongPress = (clientX: number, clientY: number) => { KeybindManager.handleInteraction( { Loading Loading @@ -289,16 +288,15 @@ export class Canvas extends EventEmitter<CanvasEvents> { getRenderer().usePixel({ x, y, hex: palette?.hex || "null" }); }; palleteCtx: IPaletteContext = {}; Pallete = { getColor: (colorId: number) => { return this.config.pallete.colors.find((c) => c.id === colorId); }, getSelectedColor: () => { if (!this.palleteCtx.color) return undefined; if (!this.cursor.color) return undefined; return this.Pallete.getColor(this.palleteCtx.color); return this.Pallete.getColor(this.cursor.color); }, getColorFromHex: (hex: string) => { Loading @@ -306,8 +304,14 @@ export class Canvas extends EventEmitter<CanvasEvents> { }, }; updatePallete(pallete: IPaletteContext) { this.palleteCtx = pallete; /** * Changes the cursor color as tracked by the Canvas instance * * @see Toolbar/Palette.tsx * @param color */ updateCursor(color?: number) { this.cursor.color = color; } place(x: number, y: number) { Loading packages/client/src/lib/keybinds.ts +10 −1 Original line number Diff line number Diff line Loading @@ -59,6 +59,11 @@ const KEYBINDS = enforceObjectType({ key: "Escape", }, ], PICK_COLOR: [ { key: "MCLICK", }, ], }); class KeybindManager_ extends EventEmitter<{ Loading @@ -72,7 +77,11 @@ class KeybindManager_ extends EventEmitter<{ passive: false, }); document.addEventListener("keyup", this.handleKeyup); document.addEventListener("click", this.handleClick); document.addEventListener("click", this.handleClick); // only gets triggered for left click document.addEventListener("auxclick", (e) => { if (e.button === 0) return; // left button still triggers this this.handleClick(e); }); } destroy() { Loading packages/lib/src/net.ts +0 −4 Original line number Diff line number Diff line Loading @@ -57,10 +57,6 @@ export interface IPosition { y: number; } export interface IPaletteContext { color?: number; } // other export type Pixel = { Loading Loading
packages/client/src/components/CanvasWrapper.tsx +53 −12 Original line number Diff line number Diff line Loading @@ -33,10 +33,6 @@ const Cursor = () => { const { cursor } = useAppContext(); const [color, setColor] = useState<string>(); useEffect(() => { console.log("color", color); }, [color]); useEffect(() => { if (typeof cursor.color === "number") { const color = Canvas.instance?.Pallete.getColor(cursor.color); Loading Loading @@ -71,17 +67,19 @@ const CanvasInner = () => { useAppContext(); const PanZoom = useContext(RendererContext); const handlePixelWhois = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { /** * Is the canvas coordinate within the bounds of the canvas? */ const isCoordInCanvas = useCallback( (x: number, y: number): boolean => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePixelWhois] canvas instance does not exist" "[CanvasWrapper#isCoordInCanvas] canvas instance does not exist" ); return; return false; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (x < 0 || y < 0) return; // discard if out of bounds if (x < 0 || y < 0) return false; // not positive, impossible to be on canvas // canvas size can dynamically change, so we need to check the current config // we're depending on canvas.instance's config so we don't have to use a react dependency Loading @@ -92,14 +90,31 @@ const CanvasInner = () => { }, } = canvas.current.getConfig(); if (x >= width || y >= height) return; // out of bounds if (x >= width || y >= height) return false; // out of bounds } else { // although this should never happen, log it console.warn( "[CanvasWrapper#handlePixelWhois] canvas config is not available yet" "[CanvasWrapper#isCoordInCanvas] canvas config is not available yet" ); } return true; }, [canvas.current] ); const handlePixelWhois = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePixelWhois] canvas instance does not exist" ); return; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (!isCoordInCanvas(x, y)) return; // out of bounds // ....... // ....... // ....... Loading @@ -114,6 +129,30 @@ const CanvasInner = () => { [canvas.current] ); const handlePickPixel = useCallback( ({ clientX, clientY }: { clientX: number; clientY: number }) => { if (!canvas.current) { console.warn( "[CanvasWrapper#handlePickPixel] canvas instance does not exist" ); return; } const [x, y] = canvas.current.screenToPos(clientX, clientY); if (!isCoordInCanvas(x, y)) return; // out of bounds const pixel = canvas.current.getPixel(x, y); if (!pixel) return; // no need to use canvas#setCursor as Palette.tsx already does that setCursor((v) => ({ ...v, color: pixel.color, })); }, [canvas.current] ); useEffect(() => { if (!canvasRef.current) return; canvas.current = new Canvas(canvasRef.current!, PanZoom); Loading @@ -126,9 +165,11 @@ const CanvasInner = () => { }); KeybindManager.on("PIXEL_WHOIS", handlePixelWhois); KeybindManager.on("PICK_COLOR", handlePickPixel); return () => { KeybindManager.off("PIXEL_WHOIS", handlePixelWhois); KeybindManager.off("PICK_COLOR", handlePickPixel); canvas.current!.destroy(); }; }, [PanZoom]); Loading
packages/client/src/components/Toolbar/Palette.tsx +8 −15 Original line number Diff line number Diff line Loading @@ -3,21 +3,14 @@ import { useAppContext } from "../../contexts/AppContext"; import { Canvas } from "../../lib/canvas"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { IPaletteContext } from "@sc07-canvas/lib/src/net"; import { KeybindManager } from "../../lib/keybinds"; export const Palette = () => { const { config, user, setCursor } = useAppContext<true>(); const [pallete, setPallete] = useState<IPaletteContext>({}); const { config, user, cursor, setCursor } = useAppContext<true>(); useEffect(() => { Canvas.instance?.updatePallete(pallete); setCursor((v) => ({ ...v, color: pallete.color, })); }, [pallete]); Canvas.instance?.updateCursor(cursor.color); }, [cursor]); useEffect(() => { const handleDeselect = () => { Loading @@ -42,8 +35,8 @@ export const Palette = () => { className="pallete-color--deselect" title="Deselect Color" onClick={() => { setPallete(({ color, ...pallete }) => { return pallete; setCursor(({ color, ...cursor }) => { return cursor; }); }} > Loading @@ -53,7 +46,7 @@ export const Palette = () => { <button key={color.id} aria-label={color.name} className={["pallete-color", color.id === pallete.color && "active"] className={["pallete-color", color.id === cursor.color && "active"] .filter((a) => a) .join(" ")} style={{ Loading @@ -61,9 +54,9 @@ export const Palette = () => { }} title={color.name} onClick={() => { setPallete((pallete) => { setCursor((cursor) => { return { ...pallete, ...cursor, color: color.id, }; }); Loading
packages/client/src/lib/canvas.ts +16 −12 Original line number Diff line number Diff line import EventEmitter from "eventemitter3"; import { ClientConfig, IPaletteContext, IPosition, Pixel, } from "@sc07-canvas/lib/src/net"; import { ClientConfig, IPosition, Pixel } from "@sc07-canvas/lib/src/net"; import Network from "./network"; import { ClickEvent, Loading Loading @@ -34,7 +29,7 @@ export class Canvas extends EventEmitter<CanvasEvents> { private canvas: HTMLCanvasElement; private PanZoom: PanZoom; private cursor = { x: -1, y: -1 }; private cursor: { x: number; y: number; color?: number } = { x: -1, y: -1 }; private pixels: { [x_y: string]: { color: number; type: "full" | "pending" }; } = {}; Loading Loading @@ -174,6 +169,10 @@ export class Canvas extends EventEmitter<CanvasEvents> { return pixels; } getPixel(x: number, y: number): { color: number } | undefined { return this.pixels[x + "_" + y]; } handleLongPress = (clientX: number, clientY: number) => { KeybindManager.handleInteraction( { Loading Loading @@ -289,16 +288,15 @@ export class Canvas extends EventEmitter<CanvasEvents> { getRenderer().usePixel({ x, y, hex: palette?.hex || "null" }); }; palleteCtx: IPaletteContext = {}; Pallete = { getColor: (colorId: number) => { return this.config.pallete.colors.find((c) => c.id === colorId); }, getSelectedColor: () => { if (!this.palleteCtx.color) return undefined; if (!this.cursor.color) return undefined; return this.Pallete.getColor(this.palleteCtx.color); return this.Pallete.getColor(this.cursor.color); }, getColorFromHex: (hex: string) => { Loading @@ -306,8 +304,14 @@ export class Canvas extends EventEmitter<CanvasEvents> { }, }; updatePallete(pallete: IPaletteContext) { this.palleteCtx = pallete; /** * Changes the cursor color as tracked by the Canvas instance * * @see Toolbar/Palette.tsx * @param color */ updateCursor(color?: number) { this.cursor.color = color; } place(x: number, y: number) { Loading
packages/client/src/lib/keybinds.ts +10 −1 Original line number Diff line number Diff line Loading @@ -59,6 +59,11 @@ const KEYBINDS = enforceObjectType({ key: "Escape", }, ], PICK_COLOR: [ { key: "MCLICK", }, ], }); class KeybindManager_ extends EventEmitter<{ Loading @@ -72,7 +77,11 @@ class KeybindManager_ extends EventEmitter<{ passive: false, }); document.addEventListener("keyup", this.handleKeyup); document.addEventListener("click", this.handleClick); document.addEventListener("click", this.handleClick); // only gets triggered for left click document.addEventListener("auxclick", (e) => { if (e.button === 0) return; // left button still triggers this this.handleClick(e); }); } destroy() { Loading
packages/lib/src/net.ts +0 −4 Original line number Diff line number Diff line Loading @@ -57,10 +57,6 @@ export interface IPosition { y: number; } export interface IPaletteContext { color?: number; } // other export type Pixel = { Loading