mirror of
https://github.com/Noratrieb/redox.git
synced 2026-01-14 16:25:04 +01:00
cool toolbar
This commit is contained in:
parent
78853473ff
commit
b754525595
11 changed files with 186 additions and 59 deletions
38
src/App.css
38
src/App.css
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,11 +22,8 @@ function App() {
|
|||
}, [canvasRef]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Redox</h1>
|
||||
<canvas onMouseMove={canvasMouseMove} onClick={canvasClick} ref={canvasRef} height={CANVAS_HEIGHT}
|
||||
width={CANVAS_WIDTH}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,3 +12,9 @@ export function circle(ctx: Ctx, x: number, y: number, r: number, color: FillSty
|
|||
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);
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Button from "./Button";
|
||||
import Button from "./main/Button";
|
||||
|
||||
export default class PauseButton extends Button {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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
|
||||
130
src/draw/ui/toolbar/Toolbar.ts
Normal file
130
src/draw/ui/toolbar/Toolbar.ts
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue