Webアプリにて、カード置き場の画像に描かれた枠内に、カード画像をきっちり並べる、というコンポーネントを実装する機会があったので、そのとき用いた方法を記します。
(フレームワークにReactを用いましたが、カードの配置自体はCSSの機能で実装可能です)
用いる画像
カードの画像:
cardBack.png / cardFace.png
ボード(カード置き場)の画像:
board.png
予めボード画像の大きさやカードの座標を以下のように調べておきます
実装コード
カード1枚の場合
まずは簡単に、1枚だけカードを配置してみます。
Boardクラス
- ボード画像を、ウィンドウの高さに合わせて表示します(height: 100vh)
- ボード画像とカード画像を重ねて表示するため、基準となる親要素のdivを作ります
- divを、子要素であるボード画像のサイズに合わせます(display: inline-block, height: 100vh)
- divを基準にカードを配置するため、position: relativeとします
Cardクラス
- カードのスタイルをcreateCardImageStyle関数を用いて作成します
- 親要素であるdiv(= ボード画像と同じサイズ)を基準に配置するため、position: absoluteとします
- カードの位置は、ボードの元画像における座標と、ボードの元画像のサイズから、割合を計算して指定します。割合で指定することにより、画面上の画像サイズに依存せずに位置を指定することができます(top、leftを%指定)
- カードのサイズも同様に、ボードの元画像における枠の比率に合わせます(widthを%指定)
- ここではカードの左上ではなく中央を基準に揃えるため、カードの高さの半分だけ上に、幅の半分だけ左にずらします(transform: translate(-50%, -50%))
index.js
import React from 'react';
import ReactDOM from 'react-dom';
const cardImage = "./cardBack.png"; // カード画像
const boardImage = "./board.png"; // ボード画像
const boardImageWidth = 1757; // ボード画像の幅(ピクセル数)
const boardImageHeight = 2126; // ボード画像の高さ(ピクセル数)
const firstLeftPosition = 318; // カードを置く座標(左から何ピクセル目か)
const firstTopPosition = 395; // カードを置く座標(上から何ピクセル目か)
const frameWidth = 340; // ボード画像に描かれたカード用の枠の幅(ピクセル数)。カード画像の幅ではないことに注意
function createCardImageStyle(leftPosition, topPosition, frameWidth) {
return {
position: "absolute",
top: (topPosition / boardImageHeight) * 100 + "%",
left: (leftPosition / boardImageWidth) * 100 + "%",
width: (frameWidth / boardImageWidth) * 100 + "%",
transform: "translate(-50%, -50%)",
};
}
class Card extends React.Component {
render(){
return (
<img src={cardImage} style={createCardImageStyle(firstLeftPosition, firstTopPosition, frameWidth)} />
);
}
}
class Board extends React.Component {
render(){
return (
<div style={{display: "inline-block", position: "relative", height: "100vh"}}>
<img src={boardImage} style={{height: "100vh"}} />
<Card/>
</div>
);
}
}
ReactDOM.render(
<React.StrictMode>
<Board />
</React.StrictMode>,
document.getElementById('root')
);
カード複数枚の場合
1枚のときと考え方は変わりません。
for文を用いて、配列にカードを格納します。
Boardクラス
- Cardの代わりにCardsコンポーネントを使います
Cardsクラス
- for文を用いて、縦横3つずつカードを作り、cards配列に格納します
- 必要な情報を、propsを経由してカードへ渡します。keyは要素の識別用です。
- srcは使用するカード画像のソースです。ここでは交互に表裏になるよう並べるため、i % 2 == j % 2による判定を入れています
- leftPositionは、1枚目のカードの座標(firstLeftPosition)に、対象のカードまでの差分(leftPositionDiff * i)を足して求めます
- topPositionも同様です
Cardクラス
- props経由で取得した情報を用いて、カードを表示します
index.js
import React from 'react';
import ReactDOM from 'react-dom';
const cardBackImage = "./cardBack.png"; // カード画像
const cardFaceimage = "./cardFace.png"; // カード画像2
const boardImage = "./board.png"; // ボード画像
const boardImageWidth = 1757; // ボード画像の幅(ピクセル数)
const boardImageHeight = 2126; // ボード画像の高さ(ピクセル数)
const firstLeftPosition = 318; // 1枚目のカードを置く座標(左から何ピクセル目か)
const firstTopPosition = 395; // 1枚目のカードを置く座標(上から何ピクセル目か)
const leftPositionDiff = 554; // カード同士の座標の差
const topPositionDiff = 682; // カード同士の座標の差
const frameWidth = 340; // ボード画像に描かれたカード用の枠の幅(ピクセル数)。カード画像の幅ではないことに注意
function createCardImageStyle(leftPosition, topPosition, frameWidth) {
return {
position: "absolute",
top: (topPosition / boardImageHeight) * 100 + "%",
left: (leftPosition / boardImageWidth) * 100 + "%",
width: (frameWidth / boardImageWidth) * 100 + "%",
transform: "translate(-50%, -50%)",
};
}
class Card extends React.Component {
render(){
return (
<img src={this.props.src} style={createCardImageStyle(this.props.leftPosition, this.props.topPosition, frameWidth)} />
);
}
}
class Cards extends React.Component {
render(){
const cards = [];
for (let j = 0; j < 3; j++){
for (let i = 0; i < 3; i++){
cards.push(<Card key={"card" + i + "_" + j} src={(i % 2 == j % 2)?cardBackImage:cardFaceimage} leftPosition={firstLeftPosition + leftPositionDiff * i} topPosition={firstTopPosition + topPositionDiff * j} />);
}
}
return (
<span>
{cards}
</span>
);
}
}
class Board extends React.Component {
render(){
return (
<div style={{display: "inline-block", position: "relative", height: "100vh"}}>
<img src={boardImage} style={{height: "100vh"}} />
<Cards/>
</div>
);
}
}
ReactDOM.render(
<React.StrictMode>
<Board />
</React.StrictMode>,
document.getElementById('root')
);