CSSのanimationとNext.jsでカート追加のアニメーション実装

# CSSのanimationとNext.jsでカート追加のアニメーション実装

`css` の `animation` を勉強しようと思い、ECサイトで商品を追加した際の動作を再現しました。  

## 概要

- `Next.js` で実装
- `ccs` の `animation` と `keyframes` を使用
- 「カート追加」ボタン押下 → 「カート追加！」をバッジで表示 → カートのカウントが増える

※ CodePenで実装しました。

## animation 使い方

`css` の `animation` は、要素に動きをつけます。
`animation-name`, `animation-delay` などのプロパティがありますが、`animation` プロパティは一括でアニメーションを指定することができます。

参照：[公式 - css animation](https://developer.mozilla.org/ja/docs/Web/CSS/animation)
  
`transition` も要素に動きをつけることができますが、`animation` のほうが `keyframes` で詳細に動きを指定することができるため、`animation` を使いました。

### 今回実装したanimation

下記が今回実装したアニメーションを付けた `css` です。
~~~scss
  .adding {
    animation: add-cart 1.8s ease-in forwards;
  }
~~~

- 第一引数: アニメーション名 - `keyframes` の名前を指定 ( [公式 - animation-name](https://developer.mozilla.org/ja/docs/Web/CSS/animation-name) )
  → `add-cart` をアニメーション名とし、`keyframes` で動きをより詳細につけます
  
- 第二引数: アニメーションの再生時間 ( [公式 - animation-duration](https://developer.mozilla.org/ja/docs/Web/CSS/animation-duration) )  
  → 1.8sと指定しました
  
- 第三引数: アニメーションの進行方法 ( [公式 - animation-timing-function](https://developer.mozilla.org/ja/docs/Web/CSS/animation-timing-function) )
  → `ease-in` と指定し、最初ゆっくりと始まり、だんだん加速してアニメーションを終了させます
  
- 第四引数: アニメーション前後のスタイル ([公式 - animation-fill-mode](https://developer.mozilla.org/ja/docs/Web/CSS/animation-fill-mode) )
  → `forwards` と指定し、`keyframes` の最後のスタイルを保持してアニメーションを終了

### 実装したkeyframes

下記が実装した `keyframes` です。

~~~scss
@keyframes add-cart {
  0% {
    opacity: 0;
    width: 0;
  }
  25%,
  75% {
    opacity: 1;
    width: 6rem;
  }
  100% {
    opacity: 0;
    width: 0;
  }
}
~~~

- 0~25%の間: `opacity` が0から1に、`width` が0から6remに変化
- 25%~75%の間: `opacity` 、 `width` どちらも変化しない
- 75%~100%の間: `opacit` が1から0に、`width` が6remから0に変化

上記によって、「カート追加！」をゆっくり伸ばして出現させ、いったん止めて、次は加速しながら縮めます。


## カート追加によるアニメーションの実装

`Next.js` と `scss` で実装しました。

~~~html
<div id="app"></div>
~~~

~~~next.js
import React, { useState, useEffect } from "https://esm.sh/react";
import ReactDOM from "https://esm.sh/react-dom";
import { Badge, Container, Button } from "https://esm.sh/react-bootstrap";

const App = () => {
  const [cartStatus, setCartStatus] = useState("empty");
  const [cartInCount, setCartInCount] = useState(0);
  const addCart = () => {
    setCartStatus("adding");
    setCartInCount(cartInCount + 1);
  };

  useEffect(() => {
    console.log(cartStatus);

    cartStatus === "adding" &&
      setTimeout(() => {
        setCartStatus("added");
      }, 1800);
  }, [cartStatus]);

  return (
    <>
      <Container className="py-2">
        <div className="header d-flex justify-content-between">
          <h1>Cart</h1>
          <Button
            type="button"
            variant="link"
            size="lg"
            className="text-dark pposition-relative"
          >
            <i className="bi-cart"></i>
            <Badge
              bg={cartStatus === "empty" ? "secondary" : "success"}
              className={`position-absolute top-25 left-0 rounded-pill ${
                cartStatus !== "empty" && "added-badge"
              }`}
            >
              {cartStatus === "adding" ? (
                <div className="added d-flex gap-1">
                  <i className="bi-check"></i>
                  <span className="adding w-0">カート追加!</span>
                </div>
              ) : (
                <span>{cartInCount}</span>
              )}
            </Badge>
          </Button>
        </div>
        <Button
          type="button"
          variant="outline-secondary"
          onClick={() => addCart()}
        >
          カート追加
        </Button>
      </Container>
    </>
  );
};

ReactDOM.render(<App />, document.querySelector("#app"));
~~~

上記では、カートが空の状態から、「カート追加」ボタン押下時に `adding` ステータスとなり、1.8s後に `added` ステータスとなりカート内のカウントが増えたバッジになるように実装しました。

~~~scss
.header {
  .adding {
    animation: add-cart 1.8s ease-in forwards;
  }
}

@keyframes add-cart {
  0% {
    opacity: 0;
    width: 0;
  }
  25%,
  75% {
    opacity: 1;
    width: 6rem;
  }
  100% {
    opacity: 0;
    width: 0;
  }
}
~~~

上記では、先ほど説明したように、add-cartアニメーション名で、1.8s間で「カート追加！」のバッジを伸縮するアニメーションを実装しました。

## まとめ

`animation` を使うことで、より詳細に動きを実装できることができました。  
`animation` プロパティでは、他にも指定できるプロパティがあるので適宜使ってみようと思います。

---

**ご覧いただきありがとうございました**

今後もフロントエンド開発で気になったことや分からないことを調べて、共有していきたいと思います。
間違いなどございましたら、教えていただきたいです。