From 7baf63f30a336cbda30b7b967e95d60076c6b70f Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Fri, 6 Feb 2026 15:37:13 +0100 Subject: [PATCH] feat(settings/options/2048): display score in top bar, and implement intro and outro animation --- .../BottomScreen/Menus/Options/2048.vue | 109 +++++++++++++----- .../bottom-screen/options/2048/frame.webp | Bin 188 -> 134 bytes 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/app/components/Settings/BottomScreen/Menus/Options/2048.vue b/app/components/Settings/BottomScreen/Menus/Options/2048.vue index 6834394..61288f6 100644 --- a/app/components/Settings/BottomScreen/Menus/Options/2048.vue +++ b/app/components/Settings/BottomScreen/Menus/Options/2048.vue @@ -12,8 +12,11 @@ const handleActivateB = () => { confirmationModal.open({ text: $t("settings.options.2048.quitConfirmation"), onConfirm: () => {}, - onClosed: (choice) => { - if (choice === "confirm") store.closeSubMenu(); + onClosed: async (choice) => { + if (choice === "confirm") { + await animateOutro(); + store.closeSubMenu(); + } }, }); }; @@ -62,9 +65,17 @@ const BOARD_X = 64; const BOARD_Y = 32; const BOARD_SIZE = 4; -const SCORE_X = 195; -const SCORE_Y = 36; -const HIGH_SCORE_Y = 68; +const SLIDE_OFFSET = 96; +const SLIDE_DURATION = 0.25; +const SCORE_OFFSET = -20; +const SCORE_DURATION = 0.15; + +const intro = reactive({ + frameOffsetY: SLIDE_OFFSET, + frameOpacity: 0, + scoreOffsetY: SCORE_OFFSET, + tilesVisible: false, +}); const emptyBoard = () => Array.from({ length: BOARD_SIZE }, () => @@ -125,12 +136,55 @@ const animateSpawnAll = () => { } }; +const animateIntro = async () => { + buildTilesFromBoard(); + + await gsap + .timeline() + .to(intro, { frameOffsetY: 0, duration: SLIDE_DURATION, ease: "none" }, 0) + .to(intro, { frameOpacity: 1, duration: SLIDE_DURATION, ease: "none" }, 0) + .call( + () => { + intro.tilesVisible = true; + animateSpawnAll(); + }, + [], + SLIDE_DURATION, + ) + .to( + intro, + { scoreOffsetY: 0, duration: SCORE_DURATION, ease: "none" }, + SLIDE_DURATION, + ); +}; + +const animateOutro = async () => { + await gsap + .timeline() + .to( + intro, + { frameOffsetY: SLIDE_OFFSET, duration: SLIDE_DURATION, ease: "none" }, + 0, + ) + .to(intro, { frameOpacity: 0, duration: SLIDE_DURATION, ease: "none" }, 0) + .to( + intro, + { scoreOffsetY: SCORE_OFFSET, duration: SCORE_DURATION, ease: "none" }, + 0, + ); +}; + +onMounted(() => { + animateIntro(); +}); + onRender((ctx) => { assets.images.home.topScreen.background.draw(ctx, 0, 0); ctx.textBaseline = "top"; ctx.save(); - ctx.translate(BOARD_X, BOARD_Y); + ctx.globalAlpha = intro.frameOpacity; + ctx.translate(BOARD_X, BOARD_Y + intro.frameOffsetY); assets.images.settings.bottomScreen.options._2048.frame.draw(ctx, -3, -3); @@ -149,6 +203,11 @@ onRender((ctx) => { } } + if (!intro.tilesVisible) { + ctx.restore(); + return; + } + for (const tile of tiles) { const color = TILE_COLORS[tile.value] ?? LAST_TILE_COLOR; @@ -174,27 +233,20 @@ onRender((ctx) => { ctx.restore(); } ctx.restore(); - - ctx.font = "7px NDS7"; - ctx.fillStyle = "#010101"; - - // score - ctx.fillText(`${$t("settings.options.2048.score")}:`, SCORE_X, SCORE_Y); - ctx.fillText(score.toString(), SCORE_X, SCORE_Y + 16); - - // high score - ctx.fillText( - `${$t("settings.options.2048.highScore")}:`, - SCORE_X, - HIGH_SCORE_Y, - ); - ctx.fillText( - savedState.value.highScore.toString(), - SCORE_X, - HIGH_SCORE_Y + 16, - ); }); +onRender((ctx) => { + ctx.translate(0, intro.scoreOffsetY); + drawButton(ctx, `${$t("settings.options.2048.score")}: ${score}`, 10, 2, 118); + drawButton( + ctx, + `${$t("settings.options.2048.highScore")}: ${savedState.value.highScore}`, + 138, + 2, + 108, + ); +}, 110); + const getCell = (row: number, col: number) => board[row]![col]!; const setCell = (row: number, col: number, val: number) => { board[row]![col] = val; @@ -297,10 +349,9 @@ const isDead = () => { }; if (board.every((r) => r.every((c) => c === 0))) { - resetBoard(); -} else { - buildTilesFromBoard(); - animateSpawnAll(); + spawnTile(); + spawnTile(); + saveState(); } const slide = (rowDir: number, colDir: number) => { diff --git a/public/nds/images/settings/bottom-screen/options/2048/frame.webp b/public/nds/images/settings/bottom-screen/options/2048/frame.webp index 7a5c6f196019e30c35b3ec4b4c6fb23b25813a9a..75ac590b8feb02f0e6876b6016308bc8ce4ea4e8 100644 GIT binary patch literal 134 zcmV;10D1pXNk&F~00012MM6+kP&iC+0000lguoyWKR_m6vGvdCCxIGd0v22UoPH7@ z6R_C&=k$}n{09K2QYF#Wf7^gf003!lZaUd-4Xl3$K4%)ye==#CM!>#_kVaNo4iF#O oBSkdlctoHzNAA>0`+om_Y5_lGdF&u^qxOaIQ=&P`eQ1vW0Hm}yBLDyZ literal 188 zcmWIYbaUInz`zjh>J$(bVBxbI$kyL-KtaIYVMSx%FZHj=4J#dCEX6AaEdEaZC3xUW zJwwAJk4