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]);
|
}, [canvasRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<canvas onMouseMove={canvasMouseMove} onClick={canvasClick} ref={canvasRef} height={CANVAS_HEIGHT}
|
||||||
<h1>Redox</h1>
|
width={CANVAS_WIDTH}/>
|
||||||
<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 {CANVAS_HEIGHT, CANVAS_WIDTH} from "../App";
|
||||||
import {MouseEvent} from "react";
|
import {MouseEvent} from "react";
|
||||||
import Vector from "./classes/Vector";
|
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 FillStyle = string | CanvasGradient | CanvasPattern;
|
||||||
type Ctx = CanvasRenderingContext2D;
|
type Ctx = CanvasRenderingContext2D;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import Particle, {colorFromCharge} from "./classes/Particle";
|
||||||
import Vector from "./classes/Vector";
|
import Vector from "./classes/Vector";
|
||||||
import {CANVAS_HEIGHT, CANVAS_WIDTH} from "../App";
|
import {CANVAS_HEIGHT, CANVAS_WIDTH} from "../App";
|
||||||
import {circle} from "./Shapes";
|
import {circle} from "./Shapes";
|
||||||
|
import {LeftClickAction} from "./ui/main/UI";
|
||||||
|
|
||||||
let particles: Particle[] = [];
|
let particles: Particle[] = [];
|
||||||
let mouseProperties: MouseProperties = {charge: 0, strength: 1, pos: new Vector()};
|
let mouseProperties: MouseProperties = {charge: 0, strength: 1, pos: new Vector()};
|
||||||
|
|
@ -26,6 +27,14 @@ interface MouseProperties {
|
||||||
pos: Vector
|
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) {
|
export function changeMouseProperties(transformer: (old: MouseProperties) => MouseProperties) {
|
||||||
mouseProperties = transformer(mouseProperties);
|
mouseProperties = transformer(mouseProperties);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,10 @@ export function circle(ctx: Ctx, x: number, y: number, r: number, color: FillSty
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.ellipse(x, y, r, r, 0, 0, 50);
|
ctx.ellipse(x, y, r, r, 0, 0, 50);
|
||||||
ctx.fill();
|
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 Vector from "../classes/Vector";
|
||||||
import {Ctx} from "../MainDraw";
|
import {Ctx} from "../MainDraw";
|
||||||
import {rect} from "../Shapes";
|
import {rect} from "../Shapes";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import Button from "./Button";
|
import Button from "./main/Button";
|
||||||
|
|
||||||
export default class PauseButton extends Button {
|
export default class PauseButton extends Button {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {Ctx} from "../MainDraw";
|
import {Ctx} from "../../MainDraw";
|
||||||
import Vector from "../classes/Vector";
|
import Vector from "../../classes/Vector";
|
||||||
import {rect} from "../Shapes";
|
import {rect} from "../../Shapes";
|
||||||
import UIComponent from "./UIComponent";
|
import UIComponent from "./UIComponent";
|
||||||
|
|
||||||
class Button extends UIComponent {
|
class Button extends UIComponent {
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
import {Ctx} from "../MainDraw";
|
import {Ctx} from "../../MainDraw";
|
||||||
import Vector from "../classes/Vector";
|
import Vector from "../../classes/Vector";
|
||||||
import {CANVAS_WIDTH} from "../../App";
|
import {CANVAS_WIDTH} from "../../../App";
|
||||||
import UIComponent from "./UIComponent";
|
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[] = [];
|
const uiComponents: UIComponent[] = [];
|
||||||
|
let defaultLeftClickAction: LeftClickAction = leftClickNoOp;
|
||||||
|
|
||||||
export function initUI() {
|
export function initUI() {
|
||||||
uiComponents.push(new MouseChargeButton(
|
uiComponents.push(new MouseChargeButton(
|
||||||
|
|
@ -15,6 +22,7 @@ export function initUI() {
|
||||||
new Vector(CANVAS_WIDTH - 60, 70),
|
new Vector(CANVAS_WIDTH - 60, 70),
|
||||||
new Vector(50, 50),
|
new Vector(50, 50),
|
||||||
));
|
));
|
||||||
|
uiComponents.push(initToolbar());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleUIMouseMove(coords: Vector) {
|
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) {
|
export function handleUIClick(coords: Vector) {
|
||||||
for (let component of uiComponents) {
|
for (let component of uiComponents) {
|
||||||
if (component.isInside(coords)) {
|
if (component.isInside(coords)) {
|
||||||
component.click();
|
component.click(coords);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invokeDefaultLeftClickAction(defaultLeftClickAction, coords);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawUI(ctx: Ctx) {
|
export function drawUI(ctx: Ctx) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import {Ctx} from "../MainDraw";
|
import {Ctx} from "../../MainDraw";
|
||||||
import Vector from "../classes/Vector";
|
import Vector from "../../classes/Vector";
|
||||||
|
|
||||||
export default abstract class UIComponent {
|
export default abstract class UIComponent {
|
||||||
protected _size: Vector;
|
protected _size: Vector;
|
||||||
|
|
@ -17,7 +17,7 @@ export default abstract class UIComponent {
|
||||||
|
|
||||||
abstract draw(ctx: Ctx): void;
|
abstract draw(ctx: Ctx): void;
|
||||||
|
|
||||||
abstract click(): void;
|
abstract click(mousePos: Vector): void;
|
||||||
|
|
||||||
onHoverEnter(): void {
|
onHoverEnter(): void {
|
||||||
this._isHovered = true;
|
this._isHovered = true;
|
||||||
|
|
@ -35,6 +35,15 @@ export default abstract class UIComponent {
|
||||||
this._wasHovered = wasHovered;
|
this._wasHovered = wasHovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get size(): Vector {
|
||||||
|
return this._size;
|
||||||
|
}
|
||||||
|
|
||||||
|
get position(): Vector {
|
||||||
|
return this._position;
|
||||||
|
}
|
||||||
|
|
||||||
public isInside(coords: Vector): boolean {
|
public isInside(coords: Vector): boolean {
|
||||||
return coords.x > this._position.x
|
return coords.x > this._position.x
|
||||||
&& coords.x < this._position.x + this._size.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