cool toolbar

This commit is contained in:
nora 2021-06-17 16:32:14 +02:00
parent 78853473ff
commit b754525595
11 changed files with 186 additions and 59 deletions

View file

@ -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);
}
}

View file

@ -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>
<canvas onMouseMove={canvasMouseMove} onClick={canvasClick} ref={canvasRef} height={CANVAS_HEIGHT}
width={CANVAS_WIDTH}/>
);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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";

View file

@ -1,5 +1,5 @@
import Button from "./Button";
import Button from "./main/Button";
export default class PauseButton extends Button {
}

View file

@ -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 {

View file

@ -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) {

View file

@ -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

View 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);
}
}