React+CSS 要素の位置を選択して切り替える

reactで要素の位置を選択して切り替える。

ReactCSSでおこなった。 reactで要素の位置を9個の選択肢から選んで変更できるようにする。選択肢は8方向と中央。

選択肢の提示方法はCSSで調整した。グリッドレイアウトを使用。 要素の配置決定についてはフレックスレイアウトを使った。

環境

環境といっても使っているのはreactくらい。

  • React 17.0

デモ

どんな風に動作するかは下のデモで確認できる。下部の9つが選択可能になっていて、クリックなどするとその場所に応じて、DEMOの位置が動く。

以降は説明と注意点、そしてデモのコードサンプルを載せておく。

react側

react側では、9つの選択肢のための要素と、選択を反映させるための要素を配置する。9つの選択肢はよくある8方向と中央という形。

配置と番号の対応付けを作る。横書きで3x3マスを左上の1から順に読んでいくイメージで対応を作る。 3は右上、9は右下といった感じ。ガラケーなどの番号配置と同じ。 デフォルトの配置は中央5の位置にしておいた。

選択肢の要素はループで作る。pos-7などのようにそれぞれに異なるクラス名を与えるようにする。CSSのクラス名で配置を実現する準備となる。 選択中の要素をわかりやすくするため、それ用のクラスも割り振る。

デモの上部には、選択に応じた配置結果を反映するための要素を配置する。 選択肢の提示(デモの下部)は配置がどういう風になるかわかりやすいような感じにする。

import { useState } from "react";
import "./styles.css";

export default function App() {
  // 選択中の位置
  const [position, setPosition] = useState(5);
  const positionDeciders = [];
  // 選択肢の要素の生成
  for (let i = 1; i < 10; i++) {
    // 選択中の要素は専用のクラスも付与する
    const divclass =
      i === position ? "position-decider selected" : "position-decider";
    positionDeciders.push(
      <div key={i} className={divclass} onClick={(e) => setPosition(i)}>
        <div className={`position-decider-demo pos-${i}`}></div>
      </div>
    );
  }
  return (
    <div className="App">
      <div className="demo-wrapper">
        <div className={`pos pos-${position}`}>DEMO</div>
      </div>
      <h2>Select one below!</h2>
      <div className="wrapper">{positionDeciders}</div>
    </div>
  );
}

CSS側

こちらの方が考えることは多い。

クラス名の整理

クラス名の整理からしていく。共通で使うクラスは位置を決めるpos-${number}${number}は1〜9の数が入る。

デモの上部で使うクラスは、demo-wrapperpos。箇条書きで説明。

  • demo-wrapper
    • 配置の変更結果を分かりやすくするためのスペースの確保用の要素のためのクラス
    • スペースの分かりやすさのためtomato色の枠線を持たせる。
    • 配置のしやすさから、フレックスレイアウトを使う。
    • もし内部要素で何も指定しない場合、中央に寄るようにする。結果的に調整しやすくなる。(これはデフォルトの指定とは関係ない)
  • pos
    • 選択肢に応じて配置を変える要素のためのクラス
    • 分かりやすさのためblue色の枠線を持たせる。

デモの下部で使うクラスは、wrapperposition-deciderposition-decider-demoselectedの4つ。箇条書きで説明。

  • wrapper
    • 選択肢全てを包むラッパ
    • 9つの選択肢の要素の配置のためグリッドレイアウトを使用して、3x3に並ぶようにする。
  • position-decider
    • 選択肢そのもの。これをクリックして配置を変えられるようにする。
    • 内部のデモ要素の配置のため、これにはフレックスレイアウトを利用する。
    • 単体の選択肢としてみれば、demo-wrapperと役割は同じ。
  • position-decider-demo
    • 選択肢内のデモのためのクラス
    • 単体の選択肢としてみれば、posと役割は同じ。
  • selected
    • 選択中であることを明示するためのクラス
    • 明示するため見た目の主張が激しくなるようにする。

グリッドとフレックスのレイアウト

グリッドレイアウトは9つの選択肢を3x3に並べるために使っているが、これはラッパとなる要素に指定するだけでいい。

グリッドに関するCSSの指定はこちらを参照: CSS グリッドレイアウト - CSS: カスケーディングスタイルシート | MDN

grid-*系の指定をするだけなので簡単だが、微調整しようと思うと少し面倒そう。今回は簡単に使うだけ。

フレックスレイアウトは要素の配置の変更に使う。 ラッパでセンタリングを行なっておいて、配置させたい要素は幅と高さを持たせつつposition: absolute;の絶対位置指定をしておく。 ラッパ側の指定でこの指定だけで中央に寄せられる。 残りの8方向については、pos-${number}でそれぞれに対応したtopleftbottomrightを使って位置を指定する。

.App {
  font-family: sans-serif;
  text-align: center;
  box-sizing: border-box;
}

.demo-wrapper {
  position: relative;
  width: 95vw;
  height: 40vh;
  margin: 0 auto;
  border: solid 1px tomato;
  display: flex;
  justify-content: center;
  align-items: center;
}

.pos {
  font-size: 2em;
  font-weight: bold;
  position: absolute;
  border: solid 1px blue;
}

.pos-1 {
  top: 0;
  left: 0;
}

.pos-2 {
  top: 0;
}

.pos-3 {
  top: 0;
  right: 0;
}

.pos-4 {
  left: 0;
}

/* .pos-5はラッパでセンタリング済みのため不要 */

.pos-6 {
  right: 0;
}

.pos-7 {
  bottom: 0;
  left: 0;
}

.pos-8 {
  bottom: 0;
}

.pos-9 {
  bottom: 0;
  right: 0;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-rows: minmax(100px, 33%);
  grid-gap: 1rem;
}

.position-decider {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  border: solid 5px brown;
  border-radius: 5px;
  cursor: pointer;
}

.position-decider-demo {
  position: absolute;
  box-sizing: border-box;
  border: solid 5px #bababa;
  border-radius: 5px;
  width: 50%;
  height: 50%;
}

.selected {
  border: solid 5px skyblue;
  box-shadow: 0 0 5px 3px lightcyan;
}

おわり

上のをコピペなどすれば大体は動くはず。

box-sizing: border-box;を使っているが、borderを使わないならなくてもいいと思う。

フレックスレイアウト以外でやると大変そうなのでやらなかった。こういう用途で配置する要素は複雑にはならないと思うので、フレックスレイアウトでいいと思う。

グリッドの方は、幅調整が雑になっていて幅がラッパの幅の33%なので、横に長い画面だと、とても選択肢が平たくなってしまう。 高さの方はminmaxで最小100px、最大33%にしているが常に100pxが使われていて%が何に対する割合か良くわかってない。 もしかしたらいつも通り親要素の高さを指定していないとうまく働かないのかもしれない?

まあそんなに横長にしないだろうからこれ以上の調整はしないでおく。 こういうGUIっぽいのを作るのはおもしろい。

以上です。


Amazonアソシエイト

https://amzn.to/3yQHm1Z

コメント

タイトルとURLをコピーしました