From f9c95993c42b5a861efa8f456d2697730f55da15 Mon Sep 17 00:00:00 2001 From: Nolan Hellyer Date: Fri, 31 Oct 2025 18:57:34 -0700 Subject: [PATCH] app area resizes, cards dynamically fan and lift on hover --- client/src/Hand.ts | 80 +++++++++++++++++++++++++++++------------ client/src/constants.ts | 2 ++ client/src/main.ts | 76 +++++++++++++++++++++++++-------------- 3 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 client/src/constants.ts diff --git a/client/src/Hand.ts b/client/src/Hand.ts index 9e8e5ad..62bedca 100644 --- a/client/src/Hand.ts +++ b/client/src/Hand.ts @@ -1,39 +1,75 @@ -import { Assets, Container, Size, Sprite } from "pixi.js"; +import { Assets, Container, Size, PointData, Sprite } from "pixi.js"; import { Card } from "./Card"; - -const CARD_WIDTH = 144; -const CARD_HEIGHT = 224; +import { CARD_HEIGHT, CARD_WIDTH } from "./constants"; const spritesheet = await Assets.load("/public/assets/cards.json"); export class Hand { - cards: Container; - #containerSize: Size; - #offset: number; - #roomRemaining: number; + #cards: Sprite[]; + #container: Container; + #maxWidth: number; + #parent: Size & PointData; constructor(maxWidth: number, cards: Card[] = []) { if (maxWidth < CARD_WIDTH) { throw new Error("hand cannot be narrower than a single card"); } - this.#roomRemaining = maxWidth; - this.#containerSize = { width: maxWidth, height: CARD_HEIGHT }; - this.#offset = CARD_WIDTH; + this.#maxWidth = maxWidth; + this.#container = new Container(); + this.#parent = { x: 0, y: 0, width: 0, height: 0 }; - const sprites: Container = new Container(); - sprites.position.set(0, 0); + if (cards.length > 0) { + const sprites: Sprite[] = []; + for (const card of cards) { + this.add(card); + } - let x = 0; - for (const card of cards) { - const sprite = new Sprite(spritesheet.textures[card]); - - sprite.x = x; - x += this.#offset; - - sprites.addChild(sprite); + this.#cards = sprites; + this.#container.addChild(...sprites); + this.fanCards(); + } else { + this.#cards = []; } + } - this.cards = sprites; + add(card: Card) { + const sprite = new Sprite(spritesheet.textures[card]); + sprite.eventMode = "dynamic"; + sprite.onmouseenter = () => { + sprite.y -= 50; + }; + sprite.onmouseleave = () => { + sprite.y += 50; + }; + this.#cards.push(sprite); + this.#container.addChild(sprite); + this.fanCards(); + this.repositionContainer(); + } + + parentArea(x: number, y: number, width: number, height: number) { + this.#parent = { x, y, width, height }; + this.repositionContainer(); + } + + repositionContainer() { + this.#container.pivot.set(this.#container.width / 2, CARD_HEIGHT); + this.#container.position.set(this.#parent.width / 2, this.#parent.height); + } + + fanCards() { + const count = this.#cards.length; + const max = this.#maxWidth; + const offset = count * CARD_WIDTH > max ? max / count : CARD_WIDTH; + let x = 0; + for (const card of this.#cards) { + card.x = x; + x += offset; + } + } + + getContainer(): Container { + return this.#container; } } diff --git a/client/src/constants.ts b/client/src/constants.ts new file mode 100644 index 0000000..adb8c09 --- /dev/null +++ b/client/src/constants.ts @@ -0,0 +1,2 @@ +export const CARD_WIDTH = 144; +export const CARD_HEIGHT = 224; diff --git a/client/src/main.ts b/client/src/main.ts index 236e8a8..b6e166e 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -1,37 +1,59 @@ -import { Application, Assets, Sprite } from "pixi.js"; +import { Application } from "pixi.js"; import { Hand } from "./Hand"; +import { Card } from "./Card"; + +const cards: Card[] = [ + "threeOfClubs", + "twoOfDiamonds", + "aceOfClubs", + "eightOfDiamonds", + "nineOfSpades", + "sixOfClubs", +]; (async () => { - // Create a new application - const app = new Application(); - - // Initialize the application - await app.init({ background: "#1099bb", resizeTo: window }); - - // Append the application canvas to the document body - document.getElementById("pixi-container")!.appendChild(app.canvas); - try { - const hand = new Hand(1000, [ - "fourOfDiamonds", - "eightOfDiamonds", - "threeOfClubs", - "kingOfSpades", - ]); + // Create and initialize a new application + const app = new Application(); + await app.init({ + background: "#338140", + width: window.innerWidth, + height: window.innerHeight, + }); - app.stage.addChild(hand.cards); + // Append the application canvas to the document body + document.getElementById("pixi-container")!.appendChild(app.canvas); - hand.cards.position.set(app.screen.width / 2, 0); - hand.cards.pivot.set(hand.cards.width / 2, 0); + const hand = new Hand(350, []); - // Listen for animate update - // app.ticker.add((time) => { - // // Just for fun, let's rotate mr rabbit a little. - // // * Delta is 1 if running at 100% performance * - // // * Creates frame-independent transformation * - // bunny.rotation += 0.1 * time.deltaTime; - // }); + hand.parentArea(0, 0, app.screen.width, app.screen.height); + + app.stage.addChild(hand.getContainer()); + + // When the user resizes the window, resize the app. There is a plugin + // for doing this automatically, but it doesn't reposition stage's + // children. Since I need those repositioned, I am handling all the + // resizing myself. Does handling it myself create some kind of strange + // issues? I sure hope not! + // + // If strange issues are encountered, check out how coordinates work in + // PixiJS: + // https://pixijs.com/8.x/guides/concepts/scene-graph#local-vs-global-coordinates + window.addEventListener?.("resize", () => { + app.renderer.resize(window.innerWidth, window.innerHeight); + hand.parentArea(0, 0, app.screen.width, app.screen.height); + }); + + let ms = 0; + let elapsed = 0; + app.ticker.add((time) => { + elapsed += time.elapsedMS; + if (cards.length && elapsed >= ms) { + ms += 300; + hand.add(cards.pop()!); + } + }); } catch (err) { - console.log(err); + console.error(err); } })();