View Transition

This commit is contained in:
Arkadiusz Sygulski
2026-02-20 23:38:23 +01:00
parent 1d8c7ce596
commit 369000961a
5 changed files with 53 additions and 12 deletions
+42 -8
View File
@@ -24,6 +24,7 @@ const CARD_BG = "bg-sky-950";
export class GameModeSelector extends LitElement {
@state() private lobbies: PublicGames | null = null;
private serverTimeOffset: number = 0;
private prevGameIds: string = "";
private lobbySocket = new PublicLobbySocket((lobbies) =>
this.handleLobbiesUpdate(lobbies),
@@ -69,14 +70,45 @@ export class GameModeSelector extends LitElement {
}
private handleLobbiesUpdate(lobbies: PublicGames) {
this.lobbies = lobbies;
this.serverTimeOffset = lobbies.serverTime - Date.now();
document.dispatchEvent(
new CustomEvent("public-lobbies-update", {
detail: { payload: lobbies },
}),
);
this.requestUpdate();
const newIds = this.extractSortedGameIds(lobbies);
const gameListChanged =
this.prevGameIds !== "" && newIds !== this.prevGameIds;
this.prevGameIds = newIds;
const applyUpdate = () => {
this.lobbies = lobbies;
this.serverTimeOffset = lobbies.serverTime - Date.now();
document.dispatchEvent(
new CustomEvent("public-lobbies-update", {
detail: { payload: lobbies },
}),
);
this.requestUpdate();
};
if (
gameListChanged &&
"startViewTransition" in document &&
typeof (document as any).startViewTransition === "function"
) {
(document as any).startViewTransition(async () => {
applyUpdate();
await this.updateComplete;
});
} else {
applyUpdate();
}
}
private extractSortedGameIds(lobbies: PublicGames): string {
const ffa = lobbies.games?.["ffa"]?.[0];
const teams = lobbies.games?.["team"]?.[0];
const special = lobbies.games?.["special"]?.[0];
return [ffa, teams, special]
.filter((g): g is PublicGameInfo => !!g)
.sort((a, b) => a.startsAt - b.startsAt)
.map((g) => g.gameID)
.join(",");
}
private getSortedLobbies(): PublicGameInfo[] {
@@ -214,6 +246,7 @@ export class GameModeSelector extends LitElement {
return html`
<button
@click=${() => this.validateAndJoin(lobby)}
style="view-transition-name: game-${lobby.gameID}"
class="group relative w-full aspect-square text-white uppercase rounded-2xl overflow-hidden transition-transform duration-200 hover:scale-[1.01] active:scale-[0.99] ring-1 ring-sky-400/30 shadow-[0_0_25px_-2px_rgba(56,189,248,0.2)] ${CARD_BG}"
>
${mapImageSrc
@@ -293,6 +326,7 @@ export class GameModeSelector extends LitElement {
return html`
<button
@click=${() => this.validateAndJoin(lobby)}
style="view-transition-name: game-${lobby.gameID}"
class="group relative w-full flex-1 text-white uppercase rounded-xl overflow-hidden transition-transform duration-200 hover:scale-[1.01] active:scale-[0.99] ${CARD_BG}"
>
${mapImageSrc
+1 -1
View File
@@ -11,7 +11,7 @@ export class PlayPage extends LitElement {
return html`
<div
id="page-play"
class="flex flex-col gap-2 w-full lg:max-w-6xl mx-auto px-0 lg:px-4 lg:my-auto min-h-0"
class="flex flex-col gap-4 w-full lg:max-w-6xl mx-auto px-0 lg:px-4 lg:my-auto min-h-0"
>
<token-login class="absolute"></token-login>
+5
View File
@@ -37,3 +37,8 @@
--medal-impossible: #d32f2f;
--medal-custom: #2196f3;
}
::view-transition-group(*) {
animation-duration: 500ms;
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
+1 -1
View File
@@ -25,7 +25,7 @@ export class DevServerConfig extends DefaultServerConfig {
}
gameCreationRate(): number {
return 5 * 1000;
return 30 * 1000;
}
numWorkers(): number {
+4 -2
View File
@@ -112,7 +112,9 @@ export class MasterLobbyService {
private async maybeScheduleLobby() {
const lobbiesByType = this.getAllLobbies();
for (const type of Object.keys(lobbiesByType) as PublicGameType[]) {
const types = Object.keys(lobbiesByType) as PublicGameType[];
for (let i = 0; i < types.length; i++) {
const type = types[i];
const lobbies = lobbiesByType[type];
if (lobbies.length >= 2) {
continue;
@@ -120,7 +122,7 @@ export class MasterLobbyService {
const lastStart = lobbies.reduce(
(max, pb) => Math.max(max, pb.startsAt),
Date.now(),
Date.now() + i * 10_000,
);
const gameID = generateID();