Freshでスクロールすると浮き上がってくるようなアニメーションを実装してみる

はじめに

みなさん、普段ウェブサイトでこのようなアニメーションを見たことがありませんか? スクロールすると要素が浮かび上がってくるようなアニメーション 今回はこのアニメーションをWeb APIのIntersection Observer APIを活用することで実装していきます!

islandで実装していく

クライアント側でのインタラクションを可能にするために、islandで作成していきます。 今回作っていくScrollRevealは次のような利用を想定しています。

<ScrollReveal>
    <div>
        {/* いろいろ */}
    </div>
</ScrollReveal>

はい、アニメーションさせたい要素をScrollRevealで囲むだけです。

下準備

まずはJSX.HTMLAttributes<HTMLDivElement>をつかって、<div>で使える属性と、ScrollReveal内で利用するデータを受け取るための型Propsを作りましょう。

import type { JSX } from "preact"; 

type Props = JSX.HTMLAttributes<HTMLDivElement> & {
    threshold?: number;			// 要素がどの程度見えてから表示するか
    once?: boolean;				// 一度だけ実行するか
};

use○○を使っていく

どの要素に対しての処理なのかをuseRefで、 要素が見えたかどうかの状態にuseStateを使います。 また、IntersectionObserverを使うために、useEffectを使います。

const ref = useRef<HTMLDivElement>(null);
const [isVisible, setVisible] = useState(false);

useEffect(()=>{
    const element = ref.current;
    if(!element) return;
    
    const observer = new IntersectionObserver(([entry])=>{
        // TODO
    }, { threshold });

    // 監視の開始
    observer.observe(element);
});

全体像

export default function ScrollReveal({
    children,
    threshold = 0.5,
    once = true,
    ...props
}: Props){
    const ref = useRef<HTMLDivElement>(null);
    const [isVisible, setVisible] = useState(false);

    useEffect(()=>{
        const element = ref.current;
        if(!element) return;

        const observer = new IntersectionObserver(([entry])=>{
            if(entry.isIntersecting){
                // 画面に入ったらvisible = trueにする
                setVisible(true);

                // 一度だけ実行なら監視を終了
                if(once){
                    observer.unobserve(element);
                }
            }else if(!once){
                setVisible(false);
            }
        }, { threshold });

        // 監視の開始
        observer.observe(element);
    });

    // isVisibleの時、アニメーションを適用
    const activeClass = isVisible
        ? `opacity-100 translate-y-0`
        : `opacity-0 translate-y-10`;

    return (
        <div
            ref={ref}
            className={`transition-all duration-750 ease-out ${activeClass}`}
            {...props}
        >
            {children}
        </div>
    );
}

実際に使ってみる

自分の使いたいところでimportして、適用したい要素を<ScrollReveal></ScrollReveal>で囲います。 thresholdonceはデフォルトでそれぞれ、0.5trueが入っているので、特に変更する必要がない場合は書かなくても大丈夫です。

<ScrollReveal>
    <div>
        {/* いろいろ */}
    </div>
</ScrollReveal>

thresholdonceを変更する場合は

<ScrollReveal threshold={0} once={false}>
    <div>
        {/* いろいろ */}
    </div>
</ScrollReveal>

などとしてください!

ブログ一覧に戻る