はじめに
みなさん、普段ウェブサイトでこのようなアニメーションを見たことがありませんか?
今回はこのアニメーションを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>で囲います。
thresholdとonceはデフォルトでそれぞれ、0.5とtrueが入っているので、特に変更する必要がない場合は書かなくても大丈夫です。
<ScrollReveal>
<div>
{/* いろいろ */}
</div>
</ScrollReveal>thresholdやonceを変更する場合は
<ScrollReveal threshold={0} once={false}>
<div>
{/* いろいろ */}
</div>
</ScrollReveal>などとしてください!