【React】Emotionでクラスを動的に変化させたり、あれこれ試してみる

2023/02/11に公開

概要

前回、Emotionの書き方を忘備録として残した。
https://zenn.dev/kiriyama/articles/630b2547a6ac79

今回は色々試していこうかと思う。
とりあえず、Emotionのオブジェクトでまとめた書き方や、CSSのanimationtransitionによるアニメーションやクリックしたらアニメーションさせる、クラスを切り替えるなどやってみる。

また今回はオブジェクトスタイルではなく、ストリングススタイルで基本的には書いていく。
*Emotionを利用する理由としては、通常のCSSのように書けるのが良いと思ってるので。

アニメーション

まずはアニメーションをやってみる。
アニメーションはanimationtransitionの2つ。

animation

animationの場合はkeyframesをCSSで下記のように設定していた。

@keyframes animation{
  0%{
  margin-left: 0;
  }
  100% {
    margin-left: 100%;
  }
}

Emotionの場合、@keyframesの代わりにkeyframesを使って下記のように書く。

const moveX = keyframes`
0%{
  margin-left: 0;
}
100% {
    margin-left: 100%;
}
`
const anime = css`
  background-color: yellow;
  width: 50px;
  height: 50px;
  animation: ${moveX} 1s linear infinite alternate backwards;
`
const App = () => {
  return (
    <>   
      <div css={anime}></div>  
    </>
  );
};

keyframesを使ってアニメーションの開始・終了の状態を設定したら。
変数animeanimationプロパティにanimation-nameにあたる${moveX}と指定する。あとはdiv要素にCSSとして指定する。

trasition

今度はtransitionについて。
こちらもEmotionの書き方さえ分かれば通常のCSSと変わらない。
例えばロールオーバーしたらちょっと大きくなるアニメーションを付け加えたいとしたら下記のようにする。

const hoverTransition = css`
  width: 50px;
  height: 50px;
  background-color: red;
  margin-left:100px;
  transition: transform 0.4s cubic-bezier(.47,1.64,.41,.8);
  &:hover {
    transform: scale(2, 2);
  }
`
const App = () => {
  return (
    <>   
      <div css={hoverTransition}></div>  
    </>
  );
};

その他の使い方

その他の使い方として、関数のようにして値を渡す方法や、クラスを追加する方法がある。

関数のように記述して引数を渡す

Emotionの場合、CSSを関数のように記述して引数を渡すことができる。
これによってクリックしたかどうかによって、背景色を変更するなどできる。

実際にクリックしたら背景色を変更する例。
ReactのuseStateを利用してtrue:falseで色を変更してみる

const setBg = (flg) =>(
  css`
    background-color: ${flg ? "red" : "blue"};
    width: 100px;
    height: 100px;
    cursor: pointer;
  `
);

const App = () => {
  const [isClick, setIsClick] = useState(false);
  const bgChangeHandler = () => {
    setIsClick(!isClick);
  };
  return (
    <>   
      <div css={setBg(isClick)} onClick={bgChangeHandler}>
          <p>背景色を帰る</p>
        </div>
    </>
  );
};

これまでの通りcssにクラスを設定するが、css={setBg(isClick)}のようにuseStateで設したisClickの値を引き渡すことができる。

あとは、CSSのbackground-colorの値を三項演算子でtrueの場合は赤、falseの場合は青と切り替えることができる。

関数のように処理をしてCSSを返す

関数で引数で渡せるので、関数内で処理できる。最終的にretrunでCSSを返してあげればいい。

  • useStateで値を変更
  • 値を関数に渡す
  • CSSを返す
const [isColor, setIsColor] = useState("1");

const onChangeColor = (e) => {
   setIsColor(e.target.value);
};

return(
 <>
  <label>
  数値:<input onChange={(e) => {onChangeColor(e);}} type="number" max="3" min="1" />
  </label>
  <div css={cssFunc(isColor)}>
    <p>このボックスの背景色を引数によって変更する</p> 
  </div>
</>
)

const cssFunc = (num) => {
  let colors = "red";
 
  if(num === "1") {
    colors = "red";
  }
  if (num === "2") {
    colors = "blue";
  }
  if (num === "3") {
    colors = "yellow";
  }
  console.log(colors);
  
  return  [
    css`
  background-color: ${colors};
   `
  ]
};

上記のように、useStateの値によってcssFuncの中で条件分岐して背景色を変えるような動的な処理も可能となる。

*フォームのtype="number"は文字列となるので注意。

クリックしたらクラスを追加する

今度はよくある、クリックしたらクラスを追加するパターン。
こちらも通常のReactでよく使われる三項演算子や論理積 (&&)による式があっていれば処理するというやつです。
下記の例は論理積 (&&)によるtrueの場合は背景色の色のクラスを追加する例。

const borderBox =css`
border:2px solid #ff0000;
padding: 10px;
width: 100px;
cursor: pointer;
transition:all 0.3s
`
const addBgRed=css`
  background-color: #ff0000;
`
const App = () => {
  const [isClass, setIsClass] = useState(false);
  
  const addClassHandler = () => {
    setAddClass(!addClass);
  };
  
  return (
    <>   
     <div css={[borderBox,isClass && addBgRed]} onClick={addClassHandler}>
         <p>ボーダーボックス。<br/>クリックしたら背景色をつける</p>
      </div>
    </>
  );
};

cssの部分でcss={[borderBox,isClass && addBgRed]}となっている部分。
isClasstrueの場合はaddBgRedというクラスを追加する。
もちろん既存のborderBoxというクラスもあるので配列にしてある。
三項演算子も同じ。
もちろんこれまでのCSSと同じようにtransitionによるアニメーションもできる。

オブジェクトにしてみる

Emotionは、CSS in JSなのでJavaScriptのように書くことができるので、関連するCSSごとにオブジェクト形式でまとめることもできる。
例えば、2カラムのボックスのレイアウトをまとめた例。


const boxLyout = {
  wrap: css`
    display: flex;
    width: 100%;
    max-width: 600px;
    margin: auto;
    justify-content: space-between;
  `,
  box: css`
    width: 45%;
    border: 3px solid #333;
    padding: 10px;
  `,
  ttl: css`
    font-weight: bold;
    font-size: 16px;
    margin-bottom: 10px;
  `,
  article: css`
    p {
      &:not(:first-child) {
        margin-top: 10px;
      }
    }
  `,
};

const App = () => {
  return (
    <>   
      <div css={boxLyout.wrap}>
      
          <div css={boxLyout.box}>
            <h2 css={boxLyout.ttl}>タイトル</h2>
            <div css={boxLyout.article}>
              <p>ここは文章ここは文章ここは文章。</p>
              <p>ここは文章ここは文章ここは文章。</p>
            </div>
          </div>

          <div css={boxLyout.box}>
            <h2 css={boxLyout.ttl}>タイトル</h2>
            <div css={boxLyout.article}>
              <p>ここは文章ここは文章ここは文章。</p>
              <p>ここは文章ここは文章ここは文章。</p>
            </div>
          </div>
	  
       </div>
    </>
  );
};

上記のようにboxLyoutというオブジェクトを作成してまとめる事ができる。
もちろん、関数のように引数を指定することなども可能。

const boxLyout = {
  box: (flg)=>(
    css`
    width: 45%;
    border: 3px solid #333;
    padding: 10px;
    cursor: pointer;
    background-color: ${flg && "red"};
  `
  )
};

まとめ

ざっとEmotionの使い方を勉強してきた。
あとは実践で学ぶしかない。
https://emotion.sh/docs/introduction
https://qiita.com/nemutas/items/dac73aa645f27cde92cd
https://scrawledtechblog.com/react-emotion-style/

Discussion