⬆︎SVGついてのまとめページ、CSSアニメーションの基本コーナーはこちら。
こんにちは、「ふ」です。
ユーザのスクロールに応じてアニメーションを発動させるための仕組み、IntersectionObserver。
webアニメーション実装においてはもはや必須のスキルとなってきています。
じつはフーノページでは以前にも、IntersectionObserverについて紹介していました⬇︎。
要素が画面内に表示されたらアニメーション、Intersection Observer。
〽️ コピペで試してみてね。
シンプルな実装であれば⬆︎の記事を参考にしていただければ、と思います。
ただし「画面の中央で発動させたい」「複数の要素をobserveしたい」などと、もっと複雑なことを望んだとき。
IntersectionObserverの深い部分まで理解しておかないと、なかなか思い通りの実装には辿り着くことができません。
そこで当ページを訪れていただいたみなさんがintersectionobserberを思い通りに使いこなせるよう、その構造についてがっつりと紹介していきたいと思います。
記事は2部構成となり、今回は「理解編」としてお届けします。
一見難解に見えるIntersectionObserverですが、いちど理解してしまえば意外と簡単なコードで実装できてしまいます。
ユーザ行動とのインタラクティブなwebアニメーションを実現するために、ぜひ学んでおきましょう。
IntersectionObserverは2つの要素、「root」と「target」が「交差しているかどうか」を常に監視しています。
そしてその状態が変化したときに、指定した関数(処理)を呼び出して実行します。
一般的な使い方であれば、rootには画面に固定されている要素、targetにはスクロールによって移動する要素を選んで指定します。
現在の状態⬆︎。
target要素とroot要素は離れていて、交差していません。
ユーザが画面をスクロールしていくと、やがてtargetはrootと交差します。
その瞬間、IntersectionObserverは
交差していない状態 ▶︎ 交差している状態
という、状態の変化に反応し、あらかじめ指定しておいた関数を呼び出して実行します。
さらにスクロールしていくと、targetはrootの領域を通り過ぎてしまいます。
このときにも
交差している状態 ▶︎ 交差していない状態
との状態変化に対して反応し、指定した関数が呼び出されます。
IntersectionObserverは、「要素どうしの交差」をきっかけとした、
イベントハンドラのような仕組みとも言えます。
ただし、「交差したら〇〇を実行」ということではありません。ここまで示したように、
「交差している」▶︎「交差していない」
「交差している」◀︎「交差していない」
というように、あくまでも状態が変化したときに発動するものである、ということを覚えておいてください。
IntersectionObserverをインタラクティブなアニメーションに利用しましょう。
root要素をどこにするかはカスタマイズすることもできますが、デフォルトではviewport(デバイスの画面)となっています。
targetを指定、IntersectionObserverを仕込んでおきます。
するとtargetがrootと交差した→つまり「targetが画面に表示された」タイミングで関数を実行することができるのです。
関数にアニメーション発火処理を仕込んでおけば、画面に表示されたときにアニメーションさせることができます。
次のセクションで、実装にあたって必要なものをみていきましょう。
IntersectionObserver、単体ではweb上でアクションを起こすことはできません。
外部の関数やtargetを用意し、それらを紐づける必要があります。
① Observerのインスタンスを生成。これが本体です。
② target要素をメソッドで指定
③ 状態変化の際に、呼び出されるcallback関数
④ 交差判定の基準を定めるoptionオブジェクト
〜辛いけど、ここを頑張って理解していきましょう。
let observer = new IntersectionObserver(callback,options);
IntersectionObserverのインスタンスを作ります。
第1引数には③の交差判定が下されたときに呼び出される関数、第2引数には交差に関する条件設定をおこなう、④のoptionオブジェクトが入ります。
「インスタンスとは?」となった方。
⬆︎のコードは「IntersectionObserverを生成する、呪文のようなもの」だと考えてもらっても、今回の実装には差し支えません(機会があれば学習しましょう)。
observer.observe(〇〇);
インスタンスのobserveメソッドで、targetとなる要素「〇〇」を指定します。
function callback(entries) { bb〜 処理内容 〜 }
交差判定が下されたときに呼び出される関数。observer本体の引数によって紐づけられます。
第1引数には、交差時の情報をまとめたIntersectionObserverEntryというものが入ります。
let options = { bbbb〜 内容 〜 bb};
交差判定の基準を細かく指定したいときに利用するオブジェクトです。本体の第2引数によって紐付け。
ルート要素をviewport以外に指定したい場合も、ここで指定します。
function callback { bb〜 処理 〜 }
callback関数は交差している/していないの状態が変化したときに実行される関数です。
関数名は、なんでも構いません。本体のobserverの第1引数に記述することによって紐付けられます。
function callback (「「1entries」」) { bb〜 処理 〜 }
callback関数の第1引数を宣言すると、そこにはIntersectionObserverEntryというものが与えられます。
またもや謎の存在が登場しました。
IntersectionObsererEntryとは、target/rootの位置や状態などの「交差部分の情報」が入ったオブジェクトです。
IntersectionObserverの実装では、この情報を利用することが多々あります。
entryの中にはたとえば、「isIntersecting」というプロパティが含まれています。
このプロパティは、「交差しているかどうか」の真偽値を返します。
このプロパティを参照し条件分岐することで「交差しているときだけ〇〇する」というような実装が可能です。
function callback(entries) { 「「1console.log(entries);」」 }
IntersectionObserverEntryは交差の状態変化が起きると、そのつど生成されます。
試しに引数を出力する処理を記述し、observerを発動させてみます。
「「3>>出力結果」」 Array [ IntersectionObserverEntry ]
出力結果をみてみると、オブジェクトを配列化したものであることがわかります。
IntersectionObserverEntryは交差状態が変化するたびに新しく生成されるにも関わらず、なぜ配列状態にする必要があるのか。
それは、⬇︎のようなケースが考えられるためです。
ターゲットが完全な横並びで2つ存在したとき、ルートに対して同時に重なることになります。
そうすると交差情報のオブジェクトは2種類のものが同時に生成されることになります。
同時に生成された2つが、InterSectionObserverEntryの配列要素として追加されることになります。
1回の生成時に2個以上の交差情報が含まれることがある、配列形式が採用されているのはこのためです。
先程console.logした内容を展開してみると、非常に多くの情報が入っていることがわかります。ここでは特によく参照しそうなもの3つを紹介しておきます。
① isInterSecting。
entries[0].isIntersecting
先ほど取り上げたプロパティ。ターゲットが「交差している/していない」を、真偽値で返します。
InterSectionObserverEntryは配列になっているため、targetが1つであっても添字 [ index ]を付けて呼び出す必要があります。
② target。
entries[0].target
targetとなっている要素を返します。
target要素はもともとobserveメソッドを使うときに取得してはいるのですが、複数のtargetに対しforEachなどを使って一括で処理をする場合には便利です。
③ interSectionRatio。
entries[0].interSectionRatio
target全体の領域に対し、rootと重なっている割合が0〜1の範囲で返ってきます。
アニメーション作成中、その挙動を検証するときに便利です。
ほかにもプロパティはたくさんありますが、頻繁に使用するのはこの3つです。
今後のアニメーション記事において、必要になるものについてはその都度紹介していこうと思います。
let options = { bbrootMargin:'〇〇', bbthreshold:〇〇, bbroot:〇〇 };
optionsオブジェクトではtargetとrootが、
「どのくらい重なると『交差した』ことにするのか」、
その条件を指定することができます。
これにより「targetが画面の上のほうにきたらアニメーション発火」「少し画面外からアニメーションさせながら画面に入ってくる」などと、細かなパフォーマンスの調整が可能に成増。
また、root要素をviewport以外にしたい場合も、ここで指定を行います。
rootMarginは、root要素に対して作用させるオプションです。「交差の有効範囲」を変化させることができます。
rootMargin:'〇〇%'
値はpx(絶対値)または%(相対値)を使用します。
記述の際は値をクオテーション「 ' ' 」「 " " 」で囲んでください。
rootMarginの初期値は0で、この場合交差の有効範囲は要素領域そのものです。
⬆︎ではrootとtargetは離れているので、もちろん「交差していない」と判断されています。
let options = { bbrootMargin:「「1"30%"」」 bb};
ここで+領域にマージンを取ってみました。
rootの有効範囲が外側に30%広がります。targetに少し被る状態となりました。
要素同志は離れているものの有効範囲が重なったため、これは「交差している」と判断されるように成増。
let options = { bbrootMargin:「「1"-20%"」」 bb};
こんどはマイナス側にマージンを取ってみます。
有効範囲が小さくなりました。
そうするとtargetはrootそのものに重なっていたとしても、有効範囲外であれば「交差していない」と判断されてしまいます。
これまでの例ではrootMarginに指定した値は「〇〇%」と1つのみでした。
しかしこれでは、上下左右にまんべんなくmarginが取られてしまいます。
特にrootMarginをマイナス側に取り、target要素が細長い場合。
2人はいつまで経っても交差することのない、運命に陥ります。
rootプロパティはCSSのmarginのように、「縦横別」「上下左右別」に指定することができます。
「「3//縦-20、横はそのまま」」 rootMargin:"-20% 0%"
縦方向のみ縮めることができました。これで細長ターゲットもキャッチできます。
こんどはtarget側に指定するプロパティ。
thresholdは「境界、しきい値」という意味で、「target領域の何パーセント以上が重なったら交差しているとみなすか」を指定します。
逆に指定以下の割合で重なっていても、「交差していない」とみなされます。
値表記は0〜1の範囲で行います。1が100%、0が0%を意味します。
let options = { threshold:0.5 }
例えば0.5(50%)とした時の動きを見てみます。
今はまだ完全に離れている状態。ここからスクロールしていきます。
少し重なりました。しかしまだtargetの25%程度しか重なっていないので、「交差した」とはみなされません。
もう少しスクロールして、targetの50%がルートに重なりました。ここで初めて「交差した」とみなされます。
その後ターゲットはルートの領域を通り過ぎていきます。
ターゲットの重なり部分が50%以下になったところで、「離れた」と判定されます。
「threshold0.5」は、「ターゲット領域の重なりが50%以上なら『交差』、50%未満なら『交差していない』と判断される」ということです。つまりターゲット領域の50%を基準に交差状態が切り替わり、コールバック関数が実行されることになります。
thresholdの初期値は「0」です。さっきの言い方で言えば「ターゲットの重なりが0%以上であれば「交差」、0%未満なら「交差していない」と判断」されます。
そのためごくわずかでも重なればコールバック関数が実行され、離れる時には完全に重なりがなくなるまで関数は実行されません。
let options = { threshold:[0.25,0.5] }
thresholdの値は複数指定することもできます。その場合、角カッコ内にカンマで区切って記述します。
するとどうなるのでしょうか?
「変化のきっかけ」が1つのターゲットに複数存在することになります。
「きっかけ」に指定されているのは、25%と50%。
それぞれの割合において「交差した/離れた」の状態変化が起きるたびにcallback関数が呼ばれます。
1 「25%重なった!」
2 「50%重なった!」
3 「重なりが50%以下になった!」
4 「重なりが25%以下になった!」
重なりの推移とともに、段階的に処理をしたい場合にはつかえそうですね(←いまのところ使い道が思いつかない「ふ」ではあります)。
let options = { root:「「5element」」 }
rootプロパティは、「交差を待ちかまえる側」をどの要素にするかを指定します。
初期値はviewport、つまりデバイスの画面となっています。
特定の領域内スクロールで交差判定したいときなどには、値に指定してやりましょう。
最後までお読みくださり、ありがとうございます。
今回はIntersectionObserverの「理解編」ということで、webアニメーションに必要な部分を重点的にお伝えしてきました。これらの内容を踏まえれば、自由なスクロール連動アニメーションを実装することができます。
準備は整いました。次は「実践編」⬇︎でお会いしましょう。
ではまた〜 ♪
IntersectionObserver、複数の要素や発生位置に対応する(実践編)。
2022.02.06
スクロールに応じたアニメーションを実践。
JavaScriptの矢印「=>」〜これはアロー関数というものです。
2022.01.09
使い方とメリットについて解説。
SVGでWeb Animations API。
2020.11.12
〽️ ネイティブJavaScriptでのアニメーション。
swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。