Use proper SVG icons in the touch control bar, center the middle cluster absolutely
Swaps the unicode glyphs (◀ ▶ ⬆) for hand-drawn stroke/fill SVG icons (chevrons, an eject-style plunger glyph, a coin glyph, a play triangle), and positions the coin/start/plunger cluster with absolute + translate so it sits on the bar's true center regardless of the flipper buttons.
This commit is contained in:
@@ -25,13 +25,62 @@ function isTouchDevice(): boolean {
|
||||
return typeof window !== "undefined" && ("ontouchstart" in window || navigator.maxTouchPoints > 0);
|
||||
}
|
||||
|
||||
function FlipperLeftIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" strokeWidth="3">
|
||||
<polyline points="15 5 8 12 15 19" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function FlipperRightIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" strokeWidth="3">
|
||||
<polyline points="9 5 16 12 9 19" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/** Eject-style glyph (triangle over a bar) for the plunger/launch control. */
|
||||
function PlungerIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
||||
<path d="M12 4.5 19 15H5l7-10.5Z" />
|
||||
<rect x="5" y="17.5" width="14" height="2.5" rx="1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function CoinIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.75">
|
||||
<circle cx="12" cy="12" r="8.5" />
|
||||
<path
|
||||
d="M14.5 9.3c-.4-.8-1.3-1.3-2.5-1.3-1.7 0-3 .9-3 2.1 0 2.9 5.5 1.3 5.5 4.1 0 1.2-1.3 2.1-3 2.1-1.2 0-2.1-.5-2.5-1.3M12 6.8v1.2M12 16v1.2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function PlayIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
||||
<path d="M7 4.5v15l13-7.5-13-7.5Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function ControlButton({
|
||||
binding,
|
||||
label,
|
||||
icon,
|
||||
size,
|
||||
}: {
|
||||
binding: KeyBinding;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
size: "lg" | "md" | "sm";
|
||||
}) {
|
||||
const [pressed, setPressed] = useState(false);
|
||||
@@ -50,28 +99,23 @@ function ControlButton({
|
||||
dispatchKey("keyup", binding);
|
||||
}, [binding]);
|
||||
|
||||
const sizeClasses =
|
||||
size === "lg"
|
||||
? "h-20 w-20 text-3xl"
|
||||
: size === "md"
|
||||
? "h-14 w-14 text-lg"
|
||||
: "h-12 w-14 font-mono text-[11px] tracking-widest";
|
||||
const sizeClasses = size === "lg" ? "h-20 w-20" : size === "md" ? "h-14 w-14" : "h-12 w-12";
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={binding.key === "Shift" ? `${label} flipper` : label}
|
||||
aria-label={label}
|
||||
onPointerDown={onDown}
|
||||
onPointerUp={onUp}
|
||||
onPointerCancel={onUp}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
className={`touch-none select-none rounded-full border-2 font-display transition ${sizeClasses} ${
|
||||
className={`flex touch-none select-none items-center justify-center rounded-full border-2 transition ${sizeClasses} ${
|
||||
pressed
|
||||
? "border-marquee bg-marquee/30 text-marquee shadow-[0_0_16px_rgba(255,122,41,0.5)]"
|
||||
: "border-chrome/40 bg-black/40 text-chrome"
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
{icon}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -97,17 +141,19 @@ export default function TouchControlBar({ active }: TouchControlBarProps) {
|
||||
return (
|
||||
<div
|
||||
data-hud
|
||||
className="absolute inset-x-0 bottom-0 z-10 flex items-center justify-between gap-2 border-t border-chrome/30 bg-ash/90 px-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur sm:hidden"
|
||||
className="absolute inset-x-0 bottom-0 z-10 flex items-center justify-between border-t border-chrome/30 bg-ash/90 px-3 pb-[max(0.75rem,env(safe-area-inset-bottom))] pt-3 backdrop-blur sm:hidden"
|
||||
>
|
||||
<ControlButton binding={LEFT_FLIPPER} label="◀" size="lg" />
|
||||
<ControlButton binding={LEFT_FLIPPER} label="Left flipper" icon={<FlipperLeftIcon />} size="lg" />
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<ControlButton binding={INSERT_COIN} label="COIN" size="sm" />
|
||||
<ControlButton binding={START_GAME} label="START" size="sm" />
|
||||
<ControlButton binding={PLUNGER} label="⬆" size="md" />
|
||||
{/* Positioned absolutely so it sits on the bar's true center, independent
|
||||
of the (equal-width, so already symmetric) flipper buttons on either side. */}
|
||||
<div className="absolute left-1/2 top-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center gap-3">
|
||||
<ControlButton binding={INSERT_COIN} label="Insert coin" icon={<CoinIcon />} size="sm" />
|
||||
<ControlButton binding={START_GAME} label="Start game" icon={<PlayIcon />} size="sm" />
|
||||
<ControlButton binding={PLUNGER} label="Plunger" icon={<PlungerIcon />} size="md" />
|
||||
</div>
|
||||
|
||||
<ControlButton binding={RIGHT_FLIPPER} label="▶" size="lg" />
|
||||
<ControlButton binding={RIGHT_FLIPPER} label="Right flipper" icon={<FlipperRightIcon />} size="lg" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user