From b754525595114e73f3f8ce7d6871b1df2c50567b Mon Sep 17 00:00:00 2001 From: Nilstrieb Date: Thu, 17 Jun 2021 16:32:14 +0200 Subject: [PATCH] cool toolbar --- src/App.css | 38 -------- src/App.tsx | 7 +- src/draw/MainDraw.ts | 2 +- src/draw/Particles.ts | 9 ++ src/draw/Shapes.ts | 6 ++ src/draw/ui/MouseChargeButton.ts | 2 +- src/draw/ui/PauseButton.ts | 4 +- src/draw/ui/{ => main}/Button.ts | 6 +- src/draw/ui/{ => main}/UI.ts | 26 ++++-- src/draw/ui/{ => main}/UIComponent.ts | 15 ++- src/draw/ui/toolbar/Toolbar.ts | 130 ++++++++++++++++++++++++++ 11 files changed, 186 insertions(+), 59 deletions(-) delete mode 100644 src/App.css rename src/draw/ui/{ => main}/Button.ts (80%) rename src/draw/ui/{ => main}/UI.ts (54%) rename src/draw/ui/{ => main}/UIComponent.ts (79%) create mode 100644 src/draw/ui/toolbar/Toolbar.ts diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.tsx b/src/App.tsx index 56bf30b..1dbfe2e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,11 +22,8 @@ function App() { }, [canvasRef]); return ( -
-

Redox

- -
+ ); } diff --git a/src/draw/MainDraw.ts b/src/draw/MainDraw.ts index 9f79b8d..8930fd4 100644 --- a/src/draw/MainDraw.ts +++ b/src/draw/MainDraw.ts @@ -3,7 +3,7 @@ import {changeMouseProperties, drawParticles, initParticles, updateParticles} fr import {CANVAS_HEIGHT, CANVAS_WIDTH} from "../App"; import {MouseEvent} from "react"; import Vector from "./classes/Vector"; -import {drawUI, handleUIClick, handleUIMouseMove, initUI} from "./ui/UI"; +import {drawUI, handleUIClick, handleUIMouseMove, initUI} from "./ui/main/UI"; type FillStyle = string | CanvasGradient | CanvasPattern; type Ctx = CanvasRenderingContext2D; diff --git a/src/draw/Particles.ts b/src/draw/Particles.ts index 9f47e03..35e2e23 100644 --- a/src/draw/Particles.ts +++ b/src/draw/Particles.ts @@ -3,6 +3,7 @@ import Particle, {colorFromCharge} from "./classes/Particle"; import Vector from "./classes/Vector"; import {CANVAS_HEIGHT, CANVAS_WIDTH} from "../App"; import {circle} from "./Shapes"; +import {LeftClickAction} from "./ui/main/UI"; let particles: Particle[] = []; let mouseProperties: MouseProperties = {charge: 0, strength: 1, pos: new Vector()}; @@ -26,6 +27,14 @@ interface MouseProperties { pos: Vector } +export function getMousePosition(): Vector { + return mouseProperties.pos; +} + +export function invokeDefaultLeftClickAction(action: LeftClickAction, mousePos: Vector) { + particles = action(mousePos, particles); +} + export function changeMouseProperties(transformer: (old: MouseProperties) => MouseProperties) { mouseProperties = transformer(mouseProperties); } diff --git a/src/draw/Shapes.ts b/src/draw/Shapes.ts index 1277af5..1b300c8 100644 --- a/src/draw/Shapes.ts +++ b/src/draw/Shapes.ts @@ -11,4 +11,10 @@ export function circle(ctx: Ctx, x: number, y: number, r: number, color: FillSty ctx.beginPath(); ctx.ellipse(x, y, r, r, 0, 0, 50); ctx.fill(); +} + +export function text(ctx: Ctx, x: number, y: number, text: string, fontSize: number = 15, color: FillStyle = "black") { + ctx.fillStyle = color; + ctx.font = `${fontSize}px Arial`; + ctx.fillText(text, x, y); } \ No newline at end of file diff --git a/src/draw/ui/MouseChargeButton.ts b/src/draw/ui/MouseChargeButton.ts index b2f6f29..1f5582b 100644 --- a/src/draw/ui/MouseChargeButton.ts +++ b/src/draw/ui/MouseChargeButton.ts @@ -1,4 +1,4 @@ -import Button from "./Button"; +import Button from "./main/Button"; import Vector from "../classes/Vector"; import {Ctx} from "../MainDraw"; import {rect} from "../Shapes"; diff --git a/src/draw/ui/PauseButton.ts b/src/draw/ui/PauseButton.ts index 09ee359..b9debcb 100644 --- a/src/draw/ui/PauseButton.ts +++ b/src/draw/ui/PauseButton.ts @@ -1,5 +1,5 @@ -import Button from "./Button"; +import Button from "./main/Button"; export default class PauseButton extends Button { - + } \ No newline at end of file diff --git a/src/draw/ui/Button.ts b/src/draw/ui/main/Button.ts similarity index 80% rename from src/draw/ui/Button.ts rename to src/draw/ui/main/Button.ts index 4e42ff3..4799e3f 100644 --- a/src/draw/ui/Button.ts +++ b/src/draw/ui/main/Button.ts @@ -1,6 +1,6 @@ -import {Ctx} from "../MainDraw"; -import Vector from "../classes/Vector"; -import {rect} from "../Shapes"; +import {Ctx} from "../../MainDraw"; +import Vector from "../../classes/Vector"; +import {rect} from "../../Shapes"; import UIComponent from "./UIComponent"; class Button extends UIComponent { diff --git a/src/draw/ui/UI.ts b/src/draw/ui/main/UI.ts similarity index 54% rename from src/draw/ui/UI.ts rename to src/draw/ui/main/UI.ts index 9fdb861..5d1ac85 100644 --- a/src/draw/ui/UI.ts +++ b/src/draw/ui/main/UI.ts @@ -1,10 +1,17 @@ -import {Ctx} from "../MainDraw"; -import Vector from "../classes/Vector"; -import {CANVAS_WIDTH} from "../../App"; +import {Ctx} from "../../MainDraw"; +import Vector from "../../classes/Vector"; +import {CANVAS_WIDTH} from "../../../App"; import UIComponent from "./UIComponent"; -import MouseChargeButton, {MouseChargeStrengthButton} from "./MouseChargeButton"; +import MouseChargeButton, {MouseChargeStrengthButton} from "../MouseChargeButton"; +import initToolbar from "../toolbar/Toolbar"; +import Particle from "../../classes/Particle"; +import {invokeDefaultLeftClickAction} from "../../Particles"; + +export type LeftClickAction = (pos: Vector, particles: Particle[]) => Particle[]; +export const leftClickNoOp: LeftClickAction = (_, p) => p; const uiComponents: UIComponent[] = []; +let defaultLeftClickAction: LeftClickAction = leftClickNoOp; export function initUI() { uiComponents.push(new MouseChargeButton( @@ -15,6 +22,7 @@ export function initUI() { new Vector(CANVAS_WIDTH - 60, 70), new Vector(50, 50), )); + uiComponents.push(initToolbar()); } export function handleUIMouseMove(coords: Vector) { @@ -29,13 +37,19 @@ export function handleUIMouseMove(coords: Vector) { } } +export function setDefaultLeftClickAction(action: LeftClickAction) { + defaultLeftClickAction = action; +} + export function handleUIClick(coords: Vector) { for (let component of uiComponents) { if (component.isInside(coords)) { - component.click(); - break; + component.click(coords); + return; } } + + invokeDefaultLeftClickAction(defaultLeftClickAction, coords); } export function drawUI(ctx: Ctx) { diff --git a/src/draw/ui/UIComponent.ts b/src/draw/ui/main/UIComponent.ts similarity index 79% rename from src/draw/ui/UIComponent.ts rename to src/draw/ui/main/UIComponent.ts index 117e0dc..877346a 100644 --- a/src/draw/ui/UIComponent.ts +++ b/src/draw/ui/main/UIComponent.ts @@ -1,5 +1,5 @@ -import {Ctx} from "../MainDraw"; -import Vector from "../classes/Vector"; +import {Ctx} from "../../MainDraw"; +import Vector from "../../classes/Vector"; export default abstract class UIComponent { protected _size: Vector; @@ -17,7 +17,7 @@ export default abstract class UIComponent { abstract draw(ctx: Ctx): void; - abstract click(): void; + abstract click(mousePos: Vector): void; onHoverEnter(): void { this._isHovered = true; @@ -35,6 +35,15 @@ export default abstract class UIComponent { this._wasHovered = wasHovered; } + + get size(): Vector { + return this._size; + } + + get position(): Vector { + return this._position; + } + public isInside(coords: Vector): boolean { return coords.x > this._position.x && coords.x < this._position.x + this._size.x diff --git a/src/draw/ui/toolbar/Toolbar.ts b/src/draw/ui/toolbar/Toolbar.ts new file mode 100644 index 0000000..d8d4f91 --- /dev/null +++ b/src/draw/ui/toolbar/Toolbar.ts @@ -0,0 +1,130 @@ +import UIComponent from "../main/UIComponent"; +import {Ctx, FillStyle} from "../../MainDraw"; +import Vector from "../../classes/Vector"; +import {rect, text} from "../../Shapes"; +import {CANVAS_HEIGHT} from "../../../App"; +import {LeftClickAction, leftClickNoOp, setDefaultLeftClickAction} from "../main/UI"; +import Particle from "../../classes/Particle"; +import {getMousePosition} from "../../Particles"; + +export default function initToolbar(): Toolbar { + const toolbar = new Toolbar( + new Vector(50, CANVAS_HEIGHT - 100), + 50 + ); + + toolbar.pushTool(new ToolbarTool( + "rgb(255,134,134)", + "rgb(255,0,0)", + (mousePos, particles) => [...particles, new Particle(mousePos, 1)], + "Create a new + particle")) + + toolbar.pushTool(new ToolbarTool( + "rgb(156,187,255)", + "rgb(0,84,255)", + (mousePos, particles) => [...particles, new Particle(mousePos, -1)], + "Create a new - particle" + )) + + toolbar.pushTool(new ToolbarTool( + "rgb(255,203,145)", + "rgb(255,169,0)", + (pos, particles) => particles.filter(p => p.position.distance(pos) > 50), + "Delete all particles near the mouse" + )); + + toolbar.pushTool(new ToolbarTool( + "rgb(152,255,185)", + "rgb(58,141,0)", + () => [], + "Delete all particles")); + + return toolbar; +} + +class Toolbar extends UIComponent { + private _tools: ToolbarTool[]; + private readonly _scale: number; + + private _activeIndex: number; + + constructor(pos: Vector, size: number) { + super(pos, new Vector(0, size)); + this._tools = []; + this._scale = size; + this._activeIndex = -1; + } + + private recalculateSize(): void { + this._size = new Vector(this._scale * this._tools.length, this._scale); + } + + pushTool(tool: ToolbarTool): void { + this._tools.push(tool); + this.recalculateSize(); + } + + click(mousePos: Vector): void { + const index = Math.floor((mousePos.x - this._position.x) / this._scale); + if (this._activeIndex === index) { + this._activeIndex = -1; + setDefaultLeftClickAction(leftClickNoOp) + return; + } + this._activeIndex = index; + setDefaultLeftClickAction(this._tools[index].leftClickAction); + } + + draw(ctx: Ctx): void { + let description = undefined; + const mousePos = getMousePosition(); + this._tools.forEach((tool, i) => { + tool.drawTool(ctx, + new Vector( + this.position.x + this.scale * i, + this.position.y + ), + this._scale, + this._activeIndex === i, + ); + const index = Math.floor((mousePos.x - this._position.x) / this._scale); + + if (index === i) { + description = tool.description; + } + }) + if (description && this._isHovered) { + text(ctx, mousePos.x, mousePos.y, description); + } + } + + get scale(): number { + return this._scale; + } +} + +class ToolbarTool { + private readonly _leftClickAction: LeftClickAction; + private readonly _color: FillStyle; + private readonly _activeColor: FillStyle; + private readonly _description: string; + + constructor(color: FillStyle, activeColor: FillStyle, leftClick: LeftClickAction, description: string) { + this._color = color; + this._activeColor = activeColor; + this._leftClickAction = leftClick; + this._description = description; + } + + get description(): string { + return this._description; + } + + get leftClickAction(): LeftClickAction { + return this._leftClickAction; + } + + drawTool(ctx: Ctx, pos: Vector, size: number, active: boolean): void { + rect(ctx, pos.x, pos.y, size, size, active ? this._activeColor : this._color); + } +} \ No newline at end of file