LoginSignup
2
2

More than 1 year has passed since last update.

画像の狙った座標に他の画像をぴったり重ねる方法

Posted at

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')
);

実行結果:

ウィンドウサイズを変更しても、ボード画像に合わせてカードが配置されます

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2