【Next.js】Imageコンポーネントのplaceholder="blur"ではブラーのアニメーションできない！どうする？

こんにちは！最近Next.jsとReactをいそいそ勉強しています、加能です。

## この記事の背景

📕[作って学ぶ Next.js/React Webサイト構築](https://amzn.asia/d/dIJZPR7)（2022年7月発売）を実践している時のこと。

このような、画像ロード後にふんわり画像が表示するアニメーションさせるには、

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250162/b755ed13-c121-6efd-8e30-32feab79d11a.gif' width='300'>

Next.jsの **`<Image>`の`placeholder="blur"`を使って、下記CSSを当てよう** とありました。

```css
span > img {
    transition: 0.2s;
}
```

しかし現在の最新バージョンのNext.jsでは、`<Image>`に`span`タグは入らなくなり、
**この方法ではアニメーションさせることができなくなりました。**

となると、

**「今`<Image>`を使って、画像ロード後にふんわり表示するにはどうしたらいいの？」**
と疑問に思ったので、調査結果をまとめました。

## 前提

| 環境          | Next.js         | ルーティング  | React（一応）    |
|---------------|-----------------|----------|----------------|
| **書籍**| v12.2.0         |  Pages Router   |v18.2.0  |
| **私**  | v14.2.3         |  App Router     |v18.3.1  |


## 結論 :pen_ballpoint: 

- `<Image>`の **`placeholder="blur"`を使ってブラーのアニメーションさせることはできない**
- 代わりに **`placeholder=blur`を使わず、ブラーをCSSでやる** と良い


## なぜ`placeholder="blur"`ではできないのか :thinking: 


`<Image>`の`placeholder="blur"`を使用すると、
ローカル画像では`blurDataUrl`というブラー画像が生成されます。

:::note warn
ちなみに外部から持ってくる画像はblurDataUrlを別途指定する必要があります。
:::

画像ローディング中はそれを使って
**`background-image: url({blurDataUrl})`がstyleとしてimgタグに付与されます。**

なので、このように読み込み中はブラーになります。

![image.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/250162/e40f80c8-a567-4433-bde0-c8e91275a74c.gif)


## background-imageは徐々に変更させられない

ですが、**このCSSの`background-image`はCSSアニメーションで徐々に変更することはができません。**


[background-imageのMDN](https://developer.mozilla.org/ja/docs/Web/CSS/background-image)では、`background-image`の公式定義では「アニメーションの種類：離散値」となっています。

アニメーションの種類の「離散」は、下記を見るとわかります。

https://developer.mozilla.org/ja/docs/Web/CSS/CSS_animated_properties



> 離散
プロパティの値は加算されず、補間は 50% で開始値から終了値に入れ替わります。

つまり、50％のタイミングでぱつっと切り替わるため、徐々に変わるようなアニメーションはできません。


## じゃあ`<Image>`を使ってブラーのアニメーションさせるには？ :point_up: 

`<Image>`を使ってふんわりブラーのアニメーションをさせるには、
そもそも **`placeholder="blur"`を使わずにブラーの段階からCSSでやるとうまくいきます。**

https://www.meje.dev/blog/nextjs-image-blur-on-load

この方のやり方のように、
useStateを使い、画像ロード完了時にClassNameを切り替えることで実現可能です。
（参考にさせていただきました :pray: ）

```jsx
// component

import { useState } from 'react';
import Image from 'next/image';
import styles from './ImageComponent.module.css';

export const ImageComponent = () => {
  const [isImageLoaded, setIsImageLoaded] = useState(false);

  return (
    <>
      <Image
        src="/path/to/image.jpg"
        alt="image alt"
        width={400}
        height={400}
        className={`${isImageLoaded ? styles.removeBlur : styles.blur}`}
        onLoad={() => setIsImageLoaded(true)}
      />
    </>
  );
};
```

```css
/* css */

.blur {
  filter: blur(8px);
  transition: filter 0.5s ease-in;
}

.removeBlur {
  filter: blur(0);
  transition: filter 0.5s ease-in;
}
```

## まとめ

**Next.jsの`<Image>`でふんわりブラーのアニメーションをさせるには、
画像をブラーさせるところから、CSSを使うと良いです。**

ちなみに古いバージョンの場合、
`span > img`のCSSでいけるのかはまだ試せてないので、そのうち試してみます。

📕[作って学ぶ Next.js/React Webサイト構築](https://amzn.asia/d/dIJZPR7)はとてもわかりやすくて楽しいです😀

もし間違いがあればコメントでご指摘ください。
お読みいただきありがとうございました。

## 参考にしたページ

https://www.meje.dev/blog/nextjs-image-blur-on-load

https://nextjs.org/docs/app/api-reference/components/image

https://developer.mozilla.org/ja/docs/Web/CSS/background-image

https://qiita.com/hibikikudo/items/6dbc560ea58cf10617fa