Skip to content

Commit

Permalink
Merge pull request #513 from Jameskmonger/fix-gameboard-scaling
Browse files Browse the repository at this point in the history
fix: scale gameboard correctly with aspect ratio
  • Loading branch information
Jameskmonger committed Aug 27, 2023
2 parents dc051d4 + ef32a35 commit 9684d8a
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from "react";

import { Meta, Story } from "@storybook/react";

import { GRID_SIZE } from "@creature-chess/models";

import { DynamicAspectRatioComponent } from "./DynamicAspectRatioComponent";

export default {
title: "@ui / DynamicAspectRatioComponent",
component: DynamicAspectRatioComponent,
} as Meta;

const Template: Story<any> = (args) => (
<div
style={{
width: args.width + "px",
height: args.height + "px",
border: "2px solid red",
}}
>
<DynamicAspectRatioComponent aspectRatio={args.aspectRatio}>
<div style={{ width: "100%", height: "100%", background: "lightblue" }}>
wawa
</div>
</DynamicAspectRatioComponent>
</div>
);

export const Portrait_FullBoard = Template.bind({});
Portrait_FullBoard.args = {
width: 300,
height: 500,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 7.2,
};

export const Portrait_HalfBoard = Template.bind({});
Portrait_HalfBoard.args = {
width: 300,
height: 500,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 4.2,
};

export const Landscape_FullBoard = Template.bind({});
Landscape_FullBoard.args = {
width: 500,
height: 300,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 7.2,
};

export const Landscape_HalfBoard = Template.bind({});
Landscape_HalfBoard.args = {
width: 500,
height: 300,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 4.2,
};

export const Square_FullBoard = Template.bind({});
Square_FullBoard.args = {
width: 400,
height: 400,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 7.2,
};

export const Square_HalfBoard = Template.bind({});
Square_HalfBoard.args = {
width: 400,
height: 400,
boardHeight: GRID_SIZE.height,
aspectRatio: 7 / 4.2,
};
75 changes: 75 additions & 0 deletions modules/@cc-web/ui/gameBoard/DynamicAspectRatioComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useRef, useEffect, useCallback } from "react";

/**
* This component will adjust its size to maintain the given aspect ratio,
* while filling its parent container as much as possible.
*
* @param aspectRatio The aspect ratio to maintain
*/
export function DynamicAspectRatioComponent({
aspectRatio,
children,
}: {
aspectRatio: number;
children: React.ReactNode;
}) {
const containerRef = useRef<HTMLDivElement>(null);
const componentRef = useRef<HTMLDivElement>(null);

const adjustComponentSize = useCallback(() => {
const container = containerRef.current;
const component = componentRef.current;

if (container && component) {
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
const containerAspectRatio = containerWidth / containerHeight;

if (aspectRatio > containerAspectRatio) {
// Width bound
component.style.width = "100%";
component.style.height = `${containerWidth / aspectRatio}px`;
} else {
// Height bound
component.style.height = "100%";
component.style.width = `${containerHeight * aspectRatio}px`;
}
}
}, [aspectRatio]);

useEffect(() => {
// Initial adjustment
adjustComponentSize();

// Listen to window resize event for edge cases (like window resizing)
window.addEventListener("resize", adjustComponentSize);

// Observe size changes in the container
const resizeObserver = new ResizeObserver(() => {
adjustComponentSize();
});

if (containerRef.current) {
resizeObserver.observe(containerRef.current);
}

return () => {
window.removeEventListener("resize", adjustComponentSize);
resizeObserver.disconnect();
};
}, [adjustComponentSize]);

return (
<div
ref={containerRef}
style={{ width: "100%", height: "100%", position: "relative" }}
>
<div
ref={componentRef}
// style={{ position: "absolute", background: "lightblue" }}
>
{children}
</div>
</div>
);
}
14 changes: 11 additions & 3 deletions modules/@cc-web/ui/gameBoard/GameBoard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { createInitialBoardState } from "@shoki/board";

import { DndProvider } from "@shoki-web/board-react";

import { Builders, GRID_SIZE, PieceModel } from "@creature-chess/models";
import {
BENCH_SLOT_COUNT,
Builders,
GRID_SIZE,
PieceModel,
} from "@creature-chess/models";

import { Piece, PieceContextProvider } from "../piece";
import { GameBoard } from "./GameBoard";
Expand Down Expand Up @@ -34,7 +39,7 @@ const Template: Story<any> = (args) => {
height: args.boardHeight,
}),
bench: createInitialBoardState<PieceModel>("bench", {
width: GRID_SIZE.width,
width: BENCH_SLOT_COUNT,
height: 1,
}),
};
Expand All @@ -45,8 +50,11 @@ const Template: Story<any> = (args) => {
[piece.id]: piece,
};

const pieceX = GRID_SIZE.width - 1;
const pieceY = args.boardHeight - 1;

context.board.piecePositions = {
"4,4": piece.id,
[`${pieceX},${pieceY}`]: piece.id,
};

return (
Expand Down
74 changes: 35 additions & 39 deletions modules/@cc-web/ui/gameBoard/GameBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
ClickBoardTileEvent,
DropBoardItemEvent,
} from "@shoki-web/board-react";
import { useElementSize } from "@shoki-web/board-react/src/useElementSize";

import { PieceModel } from "@creature-chess/models";

import { DynamicAspectRatioComponent } from "./DynamicAspectRatioComponent";
import { useGameBoard } from "./GameBoardContext";

export type GameBoardLocation =
Expand Down Expand Up @@ -138,10 +138,7 @@ function useRenderers({
return { boardPieceRenderer, benchPieceRenderer };
}

const useStyles = createUseStyles<
string,
{ isPortrait: boolean; boardWidth: number }
>({
const useStyles = createUseStyles<string>({
gameBoard: {
"height": "100%",
"width": "100%",
Expand All @@ -157,24 +154,18 @@ const useStyles = createUseStyles<
background: "#a7f070",
},
},
board: ({ isPortrait }) => ({
...(isPortrait ? {} : { height: "78%" }),

board: {
display: "flex",
justifyContent: "center",

marginBottom: "1em",
}),
bench: ({ isPortrait, boardWidth }) => ({
...(isPortrait ? {} : { height: "14%" }),
"width": `${boardWidth}px`,
"margin": "0 auto",

},
bench: {
"& .tile": {
background: "#9e9e9e !important",
boxShadow: "inset 0 0 2px #404040",
},
}),
},
});

export function GameBoard({
Expand All @@ -194,34 +185,39 @@ export function GameBoard({
onDropPiece,
});

const { ref, isPortrait, size } = useElementSize();
const styles = useStyles();

// listen to the board width and set the bench to be the same width
const { ref: boardRef, size: boardSize } = useElementSize();
/**
* How much space, in tiles, to leave between the board and the bench.
*/
const SPACER_TILE_HEIGHT = 0.2;

const styles = useStyles({ isPortrait, boardWidth: boardSize.width });
const aspectRatio =
board.size.width /
(board.size.height + bench.size.height + SPACER_TILE_HEIGHT);

return (
<div className={styles.gameBoard} ref={ref}>
<div className={styles.board}>
<BoardGrid
state={board}
onDropItem={onDropBoard}
onClickTile={onClickBoard}
renderItem={boardPieceRenderer}
scaleMode={isPortrait ? "width" : "height"}
ref={boardRef}
/>
</div>

<div className={styles.bench}>
<BoardGrid
state={bench}
onDropItem={onDropBench}
onClickTile={onClickBench}
renderItem={benchPieceRenderer}
/>
</div>
<div className={styles.gameBoard}>
<DynamicAspectRatioComponent aspectRatio={aspectRatio}>
<div className={styles.board}>
<BoardGrid
state={board}
onDropItem={onDropBoard}
onClickTile={onClickBoard}
renderItem={boardPieceRenderer}
scaleMode={"width"}
/>
</div>

<div className={styles.bench}>
<BoardGrid
state={bench}
onDropItem={onDropBench}
onClickTile={onClickBench}
renderItem={benchPieceRenderer}
/>
</div>
</DynamicAspectRatioComponent>
</div>
);
}
1 change: 0 additions & 1 deletion modules/@cc-web/ui/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { useOnClickOutside } from "./useOnClickOutside";
export { useElementSize } from "./useElementSize";
20 changes: 0 additions & 20 deletions modules/@cc-web/ui/hooks/useElementSize.ts

This file was deleted.

0 comments on commit 9684d8a

Please sign in to comment.