React で簡単にフルスクリーンのスライドショーを作る

React を使った、全画面表示のスライドショーの作り方の紹介です。
なお、iPhone は Full Screen API をサポートしてないので、この記事の内容ではフルスクリーンになりません。
Full Screen APIをサポートしているブラウザは、フルスクリーンで動きます。

# 使うライブラリ

２つのライブラリを使いました。

- [react-fullscreen](https://github.com/snakesilk/react-fullscreen#readme) : 全画面表示
- [react-slick](https://react-slick.neostack.com/) : スライドショー

# 実装サンプル

`create-react-app`で作ったものをベースにします。

```
npx create-react-app my-slideshow
cd my-slideshow
```

必要な npm パッケージを追加します。 Yarnを使っている場合は `yarn add <パッケージ名>` に読み替えてください。

```
npm install react-slick slick-carousel
npm install react-full-screen
```

スライドショーで表示する画像は、あらかじめ `public` フォルダに `img` フォルダを作って格納しておきます。

- public/img/image1.png
- public/img/image2.png
- ...

src/App.jsを、下記のように変更します。

```jsx:src/App.js
import "./App.css";
// react-slick 関連のインポート
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
// react-full-screen 関連のインポート
import { FullScreen, useFullScreenHandle } from "react-full-screen";

// 画像のURL配列(サンプルのため画像はpublicフォルダ配下に置く)
const images = [
  process.env.PUBLIC_URL + "/img/image1.png",
  process.env.PUBLIC_URL + "/img/image2.png",
  process.env.PUBLIC_URL + "/img/image3.png",
  process.env.PUBLIC_URL + "/img/image4.png",
  process.env.PUBLIC_URL + "/img/image5.png",
];

// 画像のURL配列を繰り返して、imgタグを列挙する処理
const ImageList = images.map((image, index) => (
  <div key={index}>
    <img src={image} className="SlideImage" alt="" />
  </div>
));

// 画像をスライド表示するコンポーネント
const SimpleSlider = () => {
  const settings = {
    slidesToShow: 1, // 1フレームで表示するスライド数
    slidesToScroll: 1, // 一度にスライドする数
    dots: false, // 下部のドットを非表示
    arrows: false, // 左右の<>を非表示
    adaptiveHeight: true, // 内部コンテンツに合わせてスライドの高さを自動調整
    infinite: true, // スライドを繰り返す
    lazyLoad: true, // スライド表示する時に画像を取得する
    speed: 500, // スライドアニメーションのスピード(ミリ秒)
    autoplay: true, // 自動再生
    pauseOnHover: false, // マウスカーソルをスライドにのせた時に再生を止めない
    autoplaySpeed: 8000, // 自動再生で切り替わる間隔(ミリ秒)
  };

  return <Slider {...settings}>{ImageList}</Slider>;
};

function App() {
  // フックでreact-full-screenのhandleを取得
  const handle = useFullScreenHandle();

  return (
    <div>
      <p>ボタンをクリックすると、全画面表示になるよ(iPhoneは非対応)</p>
      {/* ボタンクリック時でフルスクリーン表示 */}
      <button className="Button" onClick={handle.enter}>
        フルスクリーン
      </button>
      {/* フルスクリーン表示したい部分をFullScreenコンポーネントで囲む */}
      <FullScreen handle={handle}>
        <SimpleSlider />
        {/* 解除ボタンをフルスクリーン時に表示する */}
        {handle.active && (
          <button className="Button Exit" onClick={handle.exit}>
            フルスクリーン解除
          </button>
        )}
      </FullScreen>
    </div>
  );
}

export default App;

```

最低限スタイルを整えるために、src/App.cssを変更します。

```css:src/App.css
*:focus {
  outline: none;
}

.Button {
  margin: 12px;
}

.Button.Exit {
  position:fixed;
  top: 20px;
  left: 0px;
}

/* ブラウザ表示領域に合わせて画像を表示 */
.SlideImage {
  height: 90vh;
  width: 100vw;
  object-fit: scale-down;
}

/* フルスクリーン表示時に、画像の高さを最大化、背景を黒にする */
.fullscreen-enabled .SlideImage {
  height: 100vh;
  background: #000;
}

```

# 説明

上記のコメントでポイントは説明しているので、補足だけ。

## react-full-screen でフルスクリーン

フルスクリーン時に表示したい部分を、 `FullScreen` コンポーネントで囲みます。
フルスクリーン化は `handle.enter` 、解除は `handle.exit` です。

```jsx
function App() {
  const handle = useFullScreenHandle();

  return (
    <div>
      <button className="Button" onClick={handle.enter}>
        フルスクリーン
      </button>
      <FullScreen handle={handle}>
        <SimpleSlider />
        {handle.active && (
          <button className="Button Exit" onClick={handle.exit}>
            フルスクリーン解除
          </button>
        )}
      </FullScreen>
    </div>
  );
}
```

## react-full-screen でフルスクリーン時のスタイリング

フルスクリーン時に、fullscreen-enabledクラスの子要素になるので、子要素にスタイルがあたるように指定します。

```css
.fullscreen-enabled .SlideImage {
  height: 100vh;
  background: #000;
}
```

なお、使っているコンポーネントライブラリによっては、cssでのスタイリングとの相性が悪い場合があるかもしれません。
その場合は、jsx側でフルスクリーン化しているか判定できるので、それを判定してコンポーネントのパラメータを切り替えればなんとかなります。

## react-full-screen でフルスクリーン化しているか判定する

フルスクリーン化しているかは、`handle.active` で判定できます。
フルスクリーン化している時だけコンポーネントを表示した場合は、React の条件付きレンダリングなどで実現できます。

```jsx
        {handle.active && (
          <button className="Button Exit" onClick={handle.exit}>
            フルスクリーン解除
          </button>
        )}
```

## Full Screen API は iPhone非対応(2022/5/2時点)

Full Screen API は、iPhone 非対応です。

- https://caniuse.com/fullscreen

## react-slick のオプション

上記で全画面スライドショーぽくなるようにオプションを設定していますが、スライドショーをカスタマイズしたい場合はこのリファレンスを参照してください。

- https://react-slick.neostack.com/docs/api

## 画像の取得先

簡易のため、publicフォルダの下にあらかじめ画像をおいて、それを使うような実装例にしています。
動的に変えたい場合などは、Web APIで画像のURLリストを取得するようにします。
Web APIで画像のURLリストを取得する例については、この記事の主旨ではないので割愛します。

```jsx
const images = [
  process.env.PUBLIC_URL + "/img/image1.png",
  process.env.PUBLIC_URL + "/img/image2.png",
  process.env.PUBLIC_URL + "/img/image3.png",
  process.env.PUBLIC_URL + "/img/image4.png",
  process.env.PUBLIC_URL + "/img/image5.png",
];
```

# バージョン

上記の実装例は、下記バージョンで確認してます。

- "react": "^18.1.0"
- "react-full-screen": "^1.1.0"
- "react-slick": "^0.29.0"