Skip to content

Commit

Permalink
feat: Move the control panel to the top & auto resize the canvas to t…
Browse files Browse the repository at this point in the history
…he container
  • Loading branch information
NriotHrreion committed Nov 2, 2024
1 parent 715fd93 commit 958c093
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 117 deletions.
6 changes: 3 additions & 3 deletions src/simulator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export class Motive extends Disposable {
}

private _init(): void {
this._panel = new Panel(this._root);

this._canvas = new Canvas(this._root);
this._render = new Render(this._canvas);

this._panel = new Panel(this._root);

// Register object switchers
this._panel.addObjectSwitcher("ball", "小球", Circle, false, true);
this._panel.addObjectSwitcher("block", "木块", Box);
Expand All @@ -50,7 +50,7 @@ export class Motive extends Disposable {
this._register(this._canvas.onClick((e) => {
if(this._render.isMouseMode) return;

const obj = this._render.addObject(this._selectedObjectId, e.x, e.y);
const obj = this._render.addObject(this._selectedObjectId, e.screenX, e.screenY);
obj.setName("m");
}));

Expand Down
21 changes: 12 additions & 9 deletions src/simulator/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ interface ICanvasObject extends Renderable {
export class CanvasObject<H extends Hitbox = Hitbox> extends Disposable implements ICanvasObject {
// events
private _onPointerDown = new Emitter<PIXI.FederatedPointerEvent>();
private _onPointerMove = new Emitter<PointerEvent>();
private _onPointerUp = new Emitter<PointerEvent & { velocity: Vector }>();
private _onPointerMove = new Emitter<PIXI.FederatedPointerEvent>();
private _onPointerUp = new Emitter<PIXI.FederatedPointerEvent & { velocity: Vector }>();
private _onSettingsSave = new Emitter<ObjectSettingsList>();

protected _name?: string;
Expand Down Expand Up @@ -105,25 +105,28 @@ export class CanvasObject<H extends Hitbox = Hitbox> extends Disposable implemen
this.obj.interactive = true;
this._isInteractive = true;

this.obj.addEventListener("pointerdown", (e) => {
this.obj.on("pointerdown", (e) => {
if(!this.render.isMouseMode) return;

if(!this._isHeld) {
this._isHeld = true;

const position = this.render.container.toLocal(e.global);

this.velocity = Vector.Zero;
this.obj.x = e.clientX;
this.obj.y = e.clientY;
this.obj.x = position.x;
this.obj.y = position.y;
this.updateHitboxAnchor();
}

this._onPointerDown.fire(e);
});

document.body.addEventListener("pointermove", (e) => {
this.render.stage.on("pointermove", (e) => {
if(!this._isHeld) return;

const x = e.clientX, y = e.clientY;
const position = this.render.container.toLocal(e.global);
const x = position.x, y = position.y;

// Update position
this.obj.x = x;
Expand All @@ -145,12 +148,12 @@ export class CanvasObject<H extends Hitbox = Hitbox> extends Disposable implemen
this._mouseMovingTime = Date.now();
});

document.body.addEventListener("pointerup", (e) => {
this.render.stage.on("pointerup", (e) => {
if(!this._isHeld) return;

this._isHeld = false;
this._onPointerUp.fire({
...e,
...e as any,
velocity: this._mouseVelocity ?? Vector.Zero
});

Expand Down
6 changes: 6 additions & 0 deletions src/simulator/objects/ground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export class Ground extends CanvasObject<GroundHitbox> {

}
}));

window.addEventListener("resize", () => {
this._initTexture();
});
}

private _initTexture(): void {
Expand All @@ -74,6 +78,8 @@ export class Ground extends CanvasObject<GroundHitbox> {
const y = canvasHeight - Ground.GROUND_HEIGHT;
const obj = this.obj as PIXI.Graphics;

obj.clear();

// Horizontal line
obj.moveTo(0, canvasHeight - Ground.GROUND_HEIGHT)
.lineTo(canvasWidth, canvasHeight - Ground.GROUND_HEIGHT)
Expand Down
5 changes: 5 additions & 0 deletions src/simulator/render/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface Point {
}

interface IRender extends Renderable, IDisposable {
stage: PIXI.Container
container: PIXI.Container
canvas: Canvas

Expand Down Expand Up @@ -106,6 +107,10 @@ export class Render extends Disposable implements IRender {
}));
}

public get stage() {
return this._app.stage;
}

/**
* Initialize the whole renderer and the system when the renderer is created,
* adding something unremovable, such as ground etc.
Expand Down
20 changes: 10 additions & 10 deletions src/style/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ body {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
flex-direction: column;
box-sizing: border-box;

canvas.motive-canvas {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
.panel {
min-height: 90px;
}

.panel {
position: absolute;
bottom: 20px;
.motive-canvas-container {
flex: 1;
display: flex;
canvas.motive-canvas {
flex: 1;
}
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/ui/button/button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HoverWidget } from "@/ui/hoverWidget/hoverWidget";
import type { HoverWidget, HoverWidgetPosition } from "@/ui/hoverWidget/hoverWidget";
import type { ContextMenuItemInfo } from "@/ui/contextMenu/contextMenu";

import { createElement as createLucide, type IconNode } from "lucide";
Expand All @@ -20,13 +20,15 @@ export interface ButtonOptions {
disabled?: boolean
icon?: IconNode
tooltip?: string
tooltipPosition?: HoverWidgetPosition
contextMenuItems?: ContextMenuItemInfo[]
id?: string
}

const defaultOptions: ButtonOptions = {
variant: "secondary",
disabled: false,
tooltipPosition: "bottom-right",
contextMenuItems: []
};

Expand Down Expand Up @@ -99,8 +101,8 @@ export class Button extends Component<HTMLButtonElement, ButtonOptions> implemen

this._tooltipWidget = hoverProvider.createTextHoverWidget(this._options.tooltip, {
x: rect.left,
y: rect.top,
}, "top-right");
y: rect.top + rect.height,
}, this._options.tooltipPosition);
}));
this._register(this.onUnhover(() => {
hoverProvider.clearHoverWidgets();
Expand Down
8 changes: 6 additions & 2 deletions src/ui/canvas/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ export class Canvas extends Component<HTMLCanvasElement, CanvasOptions> implemen

private readonly _pixiOptions: Partial<PIXI.ApplicationOptions> = {
backgroundColor: 0xffffff,
resizeTo: window,
antialias: true
antialias: true,
resolution: this.ratio,
resizeTo: this._element,
};

public constructor(target: ComponentLike, _options?: CanvasOptions) {
Expand Down Expand Up @@ -96,6 +97,9 @@ export class Canvas extends Component<HTMLCanvasElement, CanvasOptions> implemen
this._element.appendChild(this._app.canvas);
this._app.canvas.classList.add("motive-canvas");
this._app.canvas.addEventListener("contextmenu", (e) => e.preventDefault());
window.addEventListener("resize", () => {
this._adaptDPR();
});

this._adaptDPR();

Expand Down
14 changes: 0 additions & 14 deletions src/ui/panel/panel.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
--mt-panel-bg: var(--tw-color-gray-100);
--mt-panel-border: var(--tw-color-gray-200);
--mt-panel-toolbar-border: var(--tw-color-gray-300);

// Panel Properties
--mt-panel-border-radius: 12px;
--mt-panel-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--mt-panel-transition-fn: ease;
--mt-panel-transition-duration: .3s;
}

.panel {
Expand All @@ -18,15 +12,7 @@
padding: 7px;
background-color: var(--mt-panel-bg);
border: 1px solid var(--mt-panel-border);
border-radius: var(--mt-panel-border-radius);
overflow: hidden;
box-shadow: var(--mt-panel-shadow);
transition: transform var(--mt-panel-transition-fn) var(--mt-panel-transition-duration);
transform: translateY(100%);

&.panel-popped-up {
transform: translateY(0);
}

&-toolbar {
display: flex;
Expand Down
80 changes: 4 additions & 76 deletions src/ui/panel/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import {
Info,
MousePointer2,
Pause,
Pin,
Play,
RotateCw,
Settings,
X
} from "lucide";

import { Emitter, type Event } from "@/common/event";
Expand All @@ -27,14 +25,10 @@ import "./panel.less";
type AvailableObjectNames = Exclude<keyof ObjectNameMap, "ground">;

export interface PanelOptions {
width?: number
height?: number

}

const defaultOptions: PanelOptions = {
width: 630,
height: 150
};
const defaultOptions: PanelOptions = { };

interface IPanel extends IComponent {
/**
Expand All @@ -49,21 +43,13 @@ interface IPanel extends IComponent {
}

export class Panel extends Component<HTMLDivElement, PanelOptions> implements IPanel {
private static readonly WITHDRAW_TIME = 1500; // ms
private _renderer: Render | null = null;

// events
private _onSelectedObjectChange = new Emitter<keyof ObjectNameMap>();

private _isPoppedUp: boolean = false;
private _isPinned: boolean = false;
private _withdrawTimer: NodeJS.Timeout | null = null;

private _mouseModeButton: Button;
private _refreshButton: Button;
private _pauseSwitcher: Switcher;
private _pinSwitcher: Switcher;
private _closeButton: Button;
private _switchers: Switcher[] = [];

public constructor(target: ComponentLike, _options?: PanelOptions) {
Expand All @@ -74,9 +60,6 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
_options
);

if(this._options.width) this._element.style.width = `${this._options.width}px`;
if(this._options.height) this._element.style.height = `${this._options.height}px`;

// UI

const toolbar = createElement("div", this);
Expand All @@ -85,18 +68,14 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
const toolbarLeftGroup = new ButtonGroup(toolbar);
toolbarLeftGroup.addButton({ icon: Settings, tooltip: "设置" }, () => modalProvider.open("settings"));
toolbarLeftGroup.addButton({ icon: Box, tooltip: "管理" }, () => modalProvider.open("manager"));
this._mouseModeButton = toolbarLeftGroup.addSwitcher({ icon: MousePointer2, tooltip: "鼠标模式" }, ({ isActive }) => this._renderer.setMouseMode(isActive));
toolbarLeftGroup.addSwitcher({ icon: MousePointer2, tooltip: "鼠标模式" }, ({ isActive }) => this._renderer.setMouseMode(isActive));
this._refreshButton = toolbarLeftGroup.addButton({ icon: RotateCw, tooltip: "刷新" });
this._pauseSwitcher = toolbarLeftGroup.addSwitcher({ icon: Pause, tooltip: "暂停" }, ({ isActive }) => {
isActive ? this._pauseRenderer() : this._unpauseRenderer();
});

const toolbarRightGroup = new ButtonGroup(toolbar);
toolbarRightGroup.addButton({ icon: Info, tooltip: "关于" }, () => modalProvider.open("about"));
this._pinSwitcher = toolbarRightGroup.addSwitcher({ icon: Pin, tooltip: "固定" }, ({ isActive }) => {
isActive ? this._pin() : this._unpin();
});
this._closeButton = toolbarRightGroup.addButton({ icon: X }, () => this._withdraw(false));
toolbarRightGroup.addButton({ icon: Info, tooltip: "关于", tooltipPosition: "top-left" }, () => modalProvider.open("about"));

const switcherContainer = createElement("div", this);
switcherContainer.classList.add("panel-switcher-container");
Expand All @@ -111,7 +90,6 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
if(this._renderer.isPaused) return;

this._renderer.refresh();
this._withdraw(false);
}
},
{ separator: true },
Expand All @@ -131,53 +109,6 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
action: () => modalProvider.open("about")
}
]);

// Listeners

this._element.addEventListener("mouseenter", () => this._popUp());
this._element.addEventListener("mouseleave", () => this._withdraw());
}

private _popUp(): void {
if(this._withdrawTimer) {
clearTimeout(this._withdrawTimer);
this._withdrawTimer = null;
}
if(this._isPoppedUp) return;

this._isPoppedUp = true;
this._element.classList.add("panel-popped-up");
}

private _withdraw(shouldTimerSet: boolean = true): void {
if(!this._isPoppedUp || this._isPinned) return;

if(shouldTimerSet) {
this._withdrawTimer = setTimeout(() => {
this._isPoppedUp = false;
this._element.classList.remove("panel-popped-up");
}, Panel.WITHDRAW_TIME);
} else {
this._isPoppedUp = false;
this._element.classList.remove("panel-popped-up");
}
}

private _pin(): void {
if(this._isPinned) return;

this._isPinned = true;
this._popUp();
this._pinSwitcher.setTooltip("取消固定");
this._closeButton.disabled = true;
}

private _unpin(): void {
if(!this._isPinned) return;

this._isPinned = false;
this._pinSwitcher.setTooltip("固定");
this._closeButton.disabled = false;
}

private _pauseRenderer(): void {
Expand All @@ -201,9 +132,6 @@ export class Panel extends Component<HTMLDivElement, PanelOptions> implements IP
this._register(this._refreshButton.onClick(() => {
this._renderer.refresh();
}));
this._register(this._mouseModeButton.onClick(() => {
this._withdraw(false);
}));

this._register(modalProvider.onModalOpen(() => this._pauseRenderer()));
this._register(modalProvider.onModalClose(() => this._unpauseRenderer()));
Expand Down

0 comments on commit 958c093

Please sign in to comment.