いまさらのスクロールするとアニメーションで現れる、の独り言
Page No.1
この記事は公開または最後に更新されてから281日が経過しています。情報が古くなっている可能性があるのでご注意下さい。
我が画像サイトを見ていて、ちょっと飽きてきたな~、などと思っていたその頃。
ネットでよく見かける、スクロールするとフワッとアニメーションで現れる効果でも付けてみようかと思ったりして。
だいたいのところこんな事をすれば、と思いつくことなので、全く難しいことでもなさそうだし、すぐにでも適用できそう。
CSS でtransition かanimation を設定しておいて、javascript のscroll イベントで変化させたいプロパティの値を変えてやるか、もしくは、animation を設定したclass をつけたり外したりする、ということでしょう。
それでは、さっそく!ちょっとこのページで試してみますか、と。
たとえばページにこんな要素があると。
実際に動かすのは、上の部分で言うと'moving'のclass とcenterpとrightp のid がついている <span>要素。スクロールイベントによってトリガーとさせるのはclass にmoving を持つ要素で、他の2つはそれに連動させて動かせば良いという意図です。その親の'posit' class のついている<div>は別にいらないんだけど、狭い画面とか画面の広さの違いに、それぞれ個々に親となるボックスに入っていたほうが、位置決めがフレキシブルにできて簡単になると思うのです。
動かす要素はposition を'absolute'にして、他の要素の位置指定に影響を及ぼさず自由に動けるようにし、それ自体の基本的な位置指定は親要素で行うほうが良いだろうという考えなのです。この場合、中身を'absolute'にすると親の中身は空っぽと同じになりぺしゃんこになってしまうので、必要なheight を指定しておく必要がありますね。それさえしておけば、ページ全体のレイアウトに影響を及ぼすことはないので、軽く動かすには必須のことであるという考えなのであります。
Press
こんな感じでしょうか。これはtransition を使っているもの。
で、これのjavascript はというと。
javascript なのでjQuery はなくても動くのですが、でも、このページはもともとjQuery のお世話になっているので、わざわざjavascript で作らなくても、とは思ったりもして。
このjavascript のコードで、window.onscroll で要素を出現させるトリガーとなる条件式においてスクロールの量という値を使ってはいません。39、41行目のところ。これに関しては、その肝となっているgetBoundingClientRect() と共にのちほど詳しく、ということに。
16行目のmoving_show_flg という配列は、動かす各要素がそれぞれ表示されているか否かのフラグを設定してる。別になくてもかまわないものではあるけれど、これがないとスクロールイベントが発生するたびに際限なくスタイルを設定しまくる、ということになってしまう。ちょっとスクロールさせるだけですぐに何百回となってしまう。なんだか無駄なことを常にやらせているようでどうにも気分が悪いのでつけています。
そして、こんどはanimation を使っているもの。animation を使えばより複雑に動かすことができますが、transition のような戻る動きは面倒みてくれない。消える時は、パッと消えてしまいます。当然のこと、区別するためにこちらは少しclass 名を変えています。
なのですが、iPhone6s の実機で試してみたところ、この下のanimation での部分は表示されませんでした。FireFox やChrome のシュミレータではまったく問題なく動いているのですがね・・・、やはりシュミレータなので、ということでしょうか。
class を付けたり取ったりするのは、setAttribute で行う方法と、コメントしてあるclassList を使う方法と両方試してみましたが、どちらもだめでした。setAttribute の方ならできそうな気がしたのですが・・・。ちなみにこれは2020/2/23 においてのこと。
ためしにjQuery でもやってみましたが、結果は同じ。クラスを追加してそのクラスに設定してあるanimation を動かすということはiPhone6s ではできていません。なにかやり方を変えないといけないのかもしれません。でも、たしか、違うページにおいては出来ていたように思うのだけれど・・・、はたしてなにが違うのだろうか・・・?
※後日追記:
後日、他のところで一定時間の間隔で、複数のクラスからその都度選択した一つをつけたりはずしたりの繰り替えしをするということをやったときのこと。
この場合だと、setAttribute だと望むべく結果が得られるものの、classList を使う方法だとあたかも止まってしまっているような状況になってしまった。で、その時々に付加されているクラスをコンソールに書き出して見ると、その理由は一目瞭然のこと理解できた。単純に基本的なところでしたね。classList.add というがごとし。
setAttribute は、既存の付属しているクラス全てにおきかえて、新しく指定されたクラスを設定する。もともとあった付いていたクラスはなくなってしまうので、スタイルなどそのクラスで設定していた場合に、見た目ももとに戻ってしまう、たぶん(試していないので・・・)。であるから、とったりつけたりするクラスはアニメーションで動く設定だけのクラスにして、スタイルはたとえばid の方で設定しておけばということだと。
いっぽう、classList.add の方は、add と言うが如しで既存のクラスはそのまま残してそれに付け加えていく。新しく付けようとしたものが既存であるならその指定は無視されるけれど、既存でないならどんどん増えていくわけ。であるから違う動きをさせようとして、新しく違うクラスを設定しても、既存のクラスもそのまま残っているから期待どうり動いてくれないということが起こる。同じ一つのクラスをつけたり外したりの繰り返しであれば問題ないのだけれど、でもまぁ、その場合はclassList.toggle を使えばよさそう。
前述のように複数のクラスをとっかえひっかえつけてはずしてと言う場合には、setAttribute を使うほうが簡単ということだけれど、問題はつけっぱなしのクラスがある場合かと。まぁ、その場合はどちらでもやりようはありそうです。
script
CSS3
で、下はjQuery で同じ処理を書いたもの。
どちらでもそんなにかわりません、どちらでも動きます。簡素に書こうと思いつつ、Object.assign なんて使ってオブジェクトを結合させてますが、この程度の文字列なら素直に並べて書いたほうがよっぽど速そうではあります・・・。
ただ、プレーンなjavascript とちょっと違うのは、上でも書いてますが、javascript においての条件式の具合。
これは、要素のページ上における位置の取得に使用しているgetBoundingClientRect() から取得できる値によることなのですが。
《 getBoundingClientRect() 》に関して
jQuery だとスクロールの量とか要素の位置を取得するのは、元々それも目的の1つだったのだと思うのだけれど、ブラウザでの違いを吸収してくれているので、とてもらくちん。いっぽう、javascript だとスクロールの量とか画面の大きさなど、ちょっと前まではそのブラウザによる違いのために、それ用の関数を作ったりしていたわけです。そして、要素の位置というのは、これまであまり使う機会がなかったように思うのですが、その要素の位置というのは何によって取得すればよいのか、ということになります。
と、いうことで探しているとこのgetBoundingClientRect() にたどりついたわけなのです。offsetTop というのも使えそうですが、これは直近の親要素との距離なので、くっついていれば0 のままだし、全くページの上端からの位置とは異なりますから使えません。
さて、このgetBoundingClientRect() なんですが、自分が持っている何冊かの教本には一切出てきません。いったいどのような値を返してくれるのでしょうか。
この語句だけで検索すると一番になっているMDN web docs によれば、「要素の寸法と、そのビューポートに対する位置を返します。」とあります。その後に説明が続くのですが、自分のつるつる脳みそではなんのことやらさっぱり???まぁ、こういうのは実際に値を見たほうが手っ取り早いというもの。
で、上述のjavascript に、次のようなスクロール量を取得する文とコンソール出力の分を加えてみました。
変数'targetoffsettop'には、動かすべき<span>要素の親である<div>要素のgetBoundingClientRect() で取得できる'top'の値がはいっています。あと、window.scrollY はIE ではundefined 。ざんねんです~!window.pageYOffset ならば大丈夫なのでそちらを使用すべきではあるけれど・・・、もういいでしょう~!
このページの上にある方の要素でデータを出しています。
No. | getBounding ClientRect().top | window.scrollY | + | --- |
1 | 3996 | 4 | 4000 | ← ページ読み込み直後、わずかにスクロール |
2 | 3987 | 13 | 4000 | |
3 | 3976 | 24 | 4000 | |
4 | 3964 | 36 | 4000 | |
5 | 3952 | 48 | 4000 | |
6 | 3942 | 58 | 4000 | |
7 | 3935 | 65 | 4000 | |
8 | 3930 | 70 | 4000 | |
9 | 3928 | 72 | 4000 | |
↓ | ||||
10 | 1077 | 2923 | 4000 | |
11 | 1048 | 2952 | 4000 | |
12 | 1028 | 2972 | 4000 | |
13 | 1018 | 2982 | 4000 | |
14 | 998 | 3002 | 4000 | |
15 | 989 | 3011 | 4000 | |
16 | 979 | 3021 | 4000 | ← 対象の要素の上端が画面下端にほぼ一致 |
17 | 949 | 3051 | 4000 | |
18 | 920 | 3080 | 4000 | |
19 | 910 | 3090 | 4000 | |
20 | 880 | 3120 | 4000 | |
↓ | ||||
21 | 211 | 3789 | 4000 | |
22 | 162 | 3838 | 4000 | |
23 | 122 | 3878 | 4000 | |
24 | 112 | 3888 | 4000 | |
25 | 63 | 3937 | 4000 | |
26 | 53 | 3947 | 4000 | |
27 | 34 | 3966 | 4000 | |
28 | 24 | 3976 | 4000 | |
29 | 18 | 3982 | 4000 | |
30 | 8 | 3992 | 4000 | ← 対象の要素の上端が画面上端にくっつきそう |
31 | -4 | 4004 | 4000 | ← 要素の上端が画面上端から消え始めている |
32 | -16 | 4016 | 4000 | |
33 | -27 | 4027 | 4000 | |
34 | -36 | 4036 | 4000 | |
35 | -43 | 4043 | 4000 | |
36 | -47 | 4047 | 4000 | |
37 | -48 | 4048 | 4000 |
で、見ると合計値がぴったし4000になっていて、これは要素のページにおける上端からの位置がぴったし4000px ということになる。ぴったりの値だと、なんだか意味のある数字のようにも思るのだけれど、全くの偶然の事であって、全く意味はないのです。小数点以下の数値を切り捨てたこともあるし、たまたま偶然に4000という数字になっているだけのことですよ。まぁねっ、偶然にも上の端からほぼ4000px の位置になっていたということではあるのですけれど・・・。だからなにっ?
ちなみにはじめに書いておくと、javascript で出した自分のモニターでの画面(ページが表示されている部分、それをviewport と言うのか)の高さ'window.innerHeight' の値は979 でした。これはちょっと重要な数値。
実際にコンソールを見ながらぐりぐりスクロールをさせていると、すぐに理解できました。
表の16番で対象としている要素の上端が画面の下端からまさに出てくる寸前の状況。この979という値は画面の高さとぴったり一致してます。
そして表の30番、31番あたりで要素の上端が画面の上端にまさにぴったりくっつき、そして画面から消え始めているという状況。ようは要素の上端と画面の上端が一致した時に、この値は0 になるということです。
このgetBoundingClientRect() から得られるtop の値は、ページの、その時画面に表示されている部分の上端から要素の上端までの距離ということになる(要素が画面に入っていなくても)。
ページを読み込んだ直後のように、スクロールが0で、表示されているのがページの上端部分であれば、その値はそのままページの上端から要素の上端の距離ということ。スクロールさせていて、表示させているのがページの中間部分であるなら、その値にスクロール量を足し合わせれば、それがページの上端から要素の上端の距離ということになるわけです。
であるならば、スクロールにおいて出現させる要素の条件判断にスクロールの量は必要ありません。
その要素が画面下端から現れるのは、getBoundingClientRect() のtop の値が、画面の高さと一致した時です。すなわち、getBoundingClientRect() のtopから画面の高さを引いた値が0になった時。で、どの辺りで対象の要素を出現させたいかということで、もう少しスクロールさせたら、たとえば画面の下端から画面の1/4ほどのところまで出てきたら出現させたいのであれば、画面の高さx1/4 の値を足しておけばいいということになる。
それが上述のjavascript の33行、35行目の部分となります。
と、いうことでgetBoundingClientRect() の正体がはっきりした。どうも、今ひとつ得体の知れないやつと付き合っているのは気持ちのわるいもので。これにて、すっきり!
Post : 2019/12/20 15:00
Comments feed
Trackback URL : https://strix.main.jp/wp-trackback.php?p=154636