🐙

ReactでCSSアニメーションを動的に書く

2022/01/11に公開

https://www.youtube.com/watch?v=ceQexYwhoTQ

位置がデータによって動的に変わる CSS アニメーションを作りたい

今回、野球の順位変動を CSS アニメーションで作ろうと考えました。
CSS アニメーションは基本的に始めから終わりまでの状態変化を記述するのと、グローバルに書くと固定アニメーションしか扱えないので、インラインでアニメーションを記述して、そこを動的に書き換えることにしました。

アニメーションする要素

親要素をposition:relativeにして、高さを50px、topを50*idx(idxは0から始まる順位インデックス)とします。
アニメーション名はここではmoveにしていますが、要素に対して1つのアニメーションを割り当てたいので、move${team.team_id}としてユニークなアニメーション名を与えます。

<div
  style={{
    border: "1px solid #555555",
    height: 50,
    padding: 8,
    display: "flex",
    width: "95vw",
    maxWidth: 375,
    position: "absolute",
    top: 50 * idx,
    animation: `move${team.team_id} 1s ease-out 0s forwards`,
  }}
>
  {myRanking(dateIdx, team.team_id) + 1}
  <CircleIcon thumbnail_url={`/images/${team.team_id}.jpg`} width={30} />
  <div
    style={{
      fontSize: 20,
      width: 200,
      overflowX: "hidden",
      whiteSpace: "nowrap",
      marginRight: 10,
    }}
  >
    {team.name}
  </div>
  <span className="number">{team.win}</span><span className="number">{team.lose}</span><span className="number">{team.draw}</span></div>

アニメーションの記述

コンポーネントに直接styleを記述します。
ranking(日付)はある日付における全チームの順位リストを返します。
myRanking(日付、チームID)は与えられた日付とチームIDで順位を返す関数です。
前回の日付と今回の日付をkeyframesの0, 100%で与えて、変動前の順位と変動後の順位で変動した座標分移動するように記述します。

const animationStyle = useMemo(() => {
    return (
      <style>
        {ranking(dateIdx).map((team) => (
          <>
            {`@keyframes move${team.team_id} {
                0% {
                    top: ${50 * myRanking(dateIdx - 1, team.team_id)}px;
                }
                50%{
                    opacity: 0.8;
                }
                100% {
                    top: ${50 * myRanking(dateIdx, team.team_id)}px;
                }
            }`}
          </>
        ))}
        {`
            span.number{
                color: #af3232;
                font-size: 20px;
            }
            `}
      </style>
    );
  }, [myRanking, dateIdx]);

CSSアニメーション、奥が深くて面白いですね!

参考リンク

https://blog.ojisan.io/inline-react-keyframe/
https://gist.github.com/yamadayuki/f1ea9ccacad7f1c140457b5877fb54cc

Discussion