0
0

Reactで自動スクロールを使う

Posted at

はじめに

自動スクロールは多くのサービスに使用されていて、多くの実装方法があったので、忘備録としてもまとめてみました。他のサイトでもよく紹介されているのが下記のような気がします。
react-scroll

window
scrollTo
scrollBy

Element
scrollIntoView
scrollTo
scrollBy

react-scroll

Reactのコンポーネントに関して自動スクロールが実装できるライブラリで、様々なスクロール方法が使用できます。今回は一部だけ紹介しますが、気になる方はnpmのドキュメント見てください。使うメリット・デメリットは下記かなと考えてます。

  • メリット
    • 様々なスクロール手法が使用できる
    • 細かい自動スクロールの設定ができる
    • 細かい自動スクロールが簡単に実装できる
  • デメリット
    • Topだけに移動するなど単純な自動スクロールに関して使う場合、標準APIの方が分かりやすい
    • 機能が多いためキャッチアップを少なからず必要となる
    • メンテされなくなる可能性がある※githubを見ると最近マージされている
    • name管理をする必要がある

実際の画面

auto-scroll.gif

実装

TanStack Routerとstyled componentsは使っていて分かりづらい部分があるが、雑に結果だけ書くと下記のようになります。
固定されたヘッダ分やtop表示か中央表示などの細かい操作ができます。(下記の場合、奇数番号のボタンを押下した場合top表示、それ以外は中央表示。)

import { createFileRoute } from "@tanstack/react-router";
import styled from "styled-components";
import { Element, scroller } from "react-scroll";

export const Route = createFileRoute("/react-scroll/")({
  component: () => {
    // 自動スクロール
    const onScroll = (isTop: boolean, name: string) => {
      scroller.scrollTo(name, {
        duration: 1000,
        delay: 100,
        smooth: true,
        offset: isTop ? -128 : -innerHeight / 3,
      });
    };
    // 表示するリスト
    const count = 10;
    const list = [];
    for (let i = 0; i < count; i++) {
      list.push(`${i + 1}番目`);
    }
    return (
      <Wrap>
        <Header>
          <Content>ヘッダ分の高さ</Content>
        </Header>
        {list.map((v, i) => (
          <Element name={`scroll-target${i}`}>
            <ScrollComponent>
              {v}
              {i + 1 < list.length && (
                <Button
                  onClick={() => {
                    const isTop = i % 2 === 0;
                    onScroll(isTop, `scroll-target${i + 1}`);
                  }}
                >
                  次へ
                </Button>
              )}
            </ScrollComponent>
          </Element>
        ))}
      </Wrap>
    );
  },
});

const ScrollComponent = styled.div`
  height: 300px;
  width: 100%;
  background-color: #fee;
  border: 2px solid;
  border-radius: 10px;
  margin: 128px auto;
  padding: 8px;
`;

const Header = styled.div`
  width: 100%;
  height: 64px;
  background-color: #ffe;
  position: fixed;
  top: 0;
  right: 0;
  border-bottom: 1px solid;
  padding: 10px;
`;

const Wrap = styled.div``;

const Content = styled.div`
  padding: 20px;
`;

const Button = styled.button`
  width: 128px;
  height: 64px;
  background-color: #ffe;
  border-color: #ffe;
  border-radius: 8px;
`;

解説

大した解説でもないですが、やっていることはreact-scrollのElementで定義されたnameをターゲットにscroller.scrollToの設定に基づいて自動スクロールしているという感じです。今回は、offset値を変更することでtop表示か中央表示かを判別しています。
今回は、自動スクロールの手法のお話なのでreact-scrollの細かい説明は割愛します。

window(scrollTo/scrollBy)

ここからは、実際の画面を作らずメリット・デメリットだけ記載しようかと思います。気になった方はコンソールを開いて実際にブラウザ上で試してください。mdnのドキュメントはscrollTo / scrollByとなります。Element指定でなく座標指定となるので、細かい挙動には向いてないです。実装としてはwindow.scrollTo(0,0)などで指定してあげるだけです。

  • メリット
    • 簡単に実装でき仕様も直感的である
    • 標準的に使えるので、基本的に使える(推奨ブラウザなどは別途参照してください)
  • デメリット
    • 細かい挙動が難しい

やはり簡単な分、細かい動きができないのでTopだけにBottomに移動させるみたいな決まった動きの場合のみ使うのが適切なパターンかなと思ってます。

Element(scrollIntoView/scrollTo/scrollBy)

mdnのドキュメントはscrollIntoView / scrollTo / scrollByとなります。Elementに紐づいて動くので、Elementを画面中央に表示させたいなど、Elementごとに使うことができるためwindowオブジェクトよりも汎用的に使えます。使い方は雑多ながら下記に記載します。

// hogehogeの要素を画面topに表示する
const scroll = document.getElementById("hogehoge").scrollIntoView({block:'start'})
〜
<button onClick={()=>{scroll()}}>ボタン</button>
〜
<div id='hogehoge'>huga</div>
  • メリット
    • 簡単に実装でき仕様も直感的である
    • Elementに応じて使用できるのでwindowよりも細かく指定できる
  • デメリット
    • react-scrollのような細かい挙動が難しい
    • 細かい挙動のドキュメントが少なく画面全体を覆うような要素に対して中央表示させる場合など実際に試してみないとわからない
    • scrollToは使えないみたいな記事や実際に使えなかったので調査する必要がある(今回は割愛)
    • id管理をする必要がある(重複すると自動スクロールしない)

scrollIntoViewに関してはオプションにdurationやoffset値が取れないため細かい動きは難しく、ページ内の特定箇所に自動スクロールなどの実装に向いていると考えています。細かい挙動や仕様で使う場合はreact-scrollの方がよさそうですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0