122 lines
3.6 KiB
JavaScript
122 lines
3.6 KiB
JavaScript
import * as Phaser from 'phaser';
|
|
import { COLORS } from '../config.js';
|
|
|
|
const RADIUS = 8;
|
|
|
|
export class Button extends Phaser.GameObjects.Container {
|
|
constructor(scene, x, y, label, onClick, options = {}) {
|
|
super(scene, x, y);
|
|
const {
|
|
width = 280,
|
|
height = 64,
|
|
bg = COLORS.panel,
|
|
bgHover = COLORS.gold,
|
|
textColor = COLORS.textHex,
|
|
textHoverColor = COLORS.textDarkHex,
|
|
fontSize = 28,
|
|
variant = 'solid',
|
|
} = options;
|
|
|
|
this.options = { width, height, bg, bgHover, textColor, textHoverColor, fontSize, variant };
|
|
|
|
const isGhost = variant === 'ghost';
|
|
|
|
this.bgRect = scene.add.graphics();
|
|
this.bgRect.postFX.addShadow(0, 3, 0.004, 1.5, 0x000000, 8, 0.65);
|
|
|
|
this._drawBg(bg, isGhost ? 0.35 : 1);
|
|
|
|
this.text = scene.add.text(0, 0, label, {
|
|
fontFamily: '"Julius Sans One"',
|
|
fontSize: `${fontSize}px`,
|
|
color: textColor,
|
|
}).setOrigin(0.5);
|
|
|
|
// Long labels on a fixed-width button otherwise render past the edge of
|
|
// the rounded rect. Purely corrective: only ever shrinks text that
|
|
// wouldn't have fit at the requested size, so every button whose label
|
|
// already fits renders exactly as before.
|
|
const maxTextWidth = width - 16;
|
|
if (this.text.width > maxTextWidth) {
|
|
const fitSize = Math.max(11, Math.floor(fontSize * (maxTextWidth / this.text.width)));
|
|
this.text.setFontSize(fitSize);
|
|
}
|
|
|
|
this.add([this.bgRect, this.text]);
|
|
|
|
this.setSize(width, height);
|
|
this.setInteractive({
|
|
useHandCursor: true,
|
|
hitArea: new Phaser.Geom.Rectangle(0, 0, width, height),
|
|
hitAreaCallback: Phaser.Geom.Rectangle.Contains,
|
|
});
|
|
|
|
const onOver = () => {
|
|
if (this._active) return;
|
|
const { bgHover: bgh, textHoverColor: thc, variant: v } = this.options;
|
|
if (v === 'ghost') {
|
|
this._drawBg(bgh, 0.9);
|
|
this.text.setColor(thc);
|
|
} else {
|
|
this._drawBg(bgh, 1);
|
|
this.text.setColor(thc);
|
|
}
|
|
};
|
|
const onOut = () => {
|
|
if (this._active) return;
|
|
const { bg: b, textColor: tc, variant: v } = this.options;
|
|
this._drawBg(b, v === 'ghost' ? 0.35 : 1);
|
|
this.text.setColor(tc);
|
|
};
|
|
const onDown = () => this.bgRect.setScale(0.97);
|
|
const onUp = () => this.bgRect.setScale(1);
|
|
|
|
this.on('pointerover', onOver);
|
|
this.on('pointerout', onOut);
|
|
this.on('pointerdown', onDown);
|
|
this.on('pointerup', onUp);
|
|
this.on('pointerupoutside', onUp);
|
|
if (onClick) this.on('pointerup', onClick);
|
|
|
|
scene.add.existing(this);
|
|
}
|
|
|
|
_drawBg(fillColor, fillAlpha) {
|
|
const { width, height } = this.options;
|
|
const hw = width / 2;
|
|
const hh = height / 2;
|
|
this.bgRect.clear();
|
|
if (fillAlpha > 0) {
|
|
this.bgRect.fillStyle(fillColor, fillAlpha);
|
|
this.bgRect.fillRoundedRect(-hw, -hh, width, height, RADIUS);
|
|
}
|
|
this.bgRect.lineStyle(2, COLORS.accent, 1);
|
|
this.bgRect.strokeRoundedRect(-hw, -hh, width, height, RADIUS);
|
|
}
|
|
|
|
setActive(active) {
|
|
this._active = active;
|
|
const { bg, bgHover, textColor, textHoverColor, variant } = this.options;
|
|
if (active) {
|
|
this._drawBg(bgHover, 1);
|
|
this.text.setColor(textHoverColor);
|
|
} else {
|
|
this._drawBg(bg, variant === 'ghost' ? 0.35 : 1);
|
|
this.text.setColor(textColor);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
setLabel(label) {
|
|
this.text.setText(label);
|
|
return this;
|
|
}
|
|
|
|
setEnabled(enabled) {
|
|
this.setAlpha(enabled ? 1 : 0.5);
|
|
if (enabled) this.setInteractive({ useHandCursor: true });
|
|
else this.disableInteractive();
|
|
return this;
|
|
}
|
|
}
|