脱jQuery第2弾。スムーススクロールjQueryなしで実装していきます。
意外にこれって大変でした。。
制作コード
今回作ったコードは以下のものになります。
// 関数定義 -------------------------
const smoothScroll = () =>{
let links = document.querySelectorAll('a[href^="#"]');
const speed = 200; // スクロールスピード
const divisor = 100; // 分割数
const tolerance = 5; // 許容誤差
const headerHeight = 0; // 固定ヘッダーがある場合はその高さ分ずらす
const interval = speed/divisor;
for(let i = 0; i < links.length; i++){
links[i].addEventListener('click',(e)=>{
e.preventDefault();
let nowY = window.pageYOffset;
const href = e.currentTarget.getAttribute('href');
const target = document.querySelector(href);
if( target != null){
const targetRectTop = target.getBoundingClientRect().top;
const targetY = targetRectTop + nowY - headerHeight;
const minY = Math.abs((targetY - nowY)/divisor);
doScroll(minY,nowY,targetY,tolerance,interval);
}
});
}
}
const doScroll = (minY,nowY,targetY,tolerance,interval) =>{
let toY ;
if( targetY < nowY ){
toY = nowY - minY;
}else{
toY = nowY + minY;
}
window.scrollTo(0, toY);
if( targetY - tolerance > toY || toY > targetY + tolerance){
window.setTimeout(doScroll,interval,minY,toY,targetY,tolerance,interval);
}else{
return false;
}
}
// 関数実行 -------------------------
smoothScroll();
コードの説明
コードを見ながらのほうが説明は読みやすいと思うので、ここから先を読む人はポップアップでコードを出して読んでください。
smoothScroll関数
4~8行目
2〜23行目にあるsmoothScroll関数について簡単に説明していきます。
スクロールのスピードを決めているのは、4行目のspeedの値です。
5行目の分割数というのは、目的の要素までたどり着くまでに何回 window.scrollTo();を実行するかということです。
例えばアンカーボタンを押した時に
アンカーの場所まで1000pxの時には
現在の設定値だと、window.scrollTo(0,10px);を2ミリ秒ごとに100回繰り返して、アンカーの場所まで移動します。
設定値はお好みで変えてみてください。
6行目 toleranceはズレの許容誤差です。毎回綺麗に割り切れる数字で分割される訳ではないので、アンカーの±toleranceまで来たら、アンカーのところまでスクロールさせて処理を終了させます。
7行目は固定ヘッダーがあった場合に、ヘッダーの高さを入れます。
固定ヘッダーがある場合は、その高さ分を考慮してアンカーの場所をずらします。
ヘッダーが可変の高さの場合は、7行目を下記のように書き換えればいいと思います。
const header = document.getElementById('ヘッダーのid');
const headerHeight = header.clientHeight;
11~20行目
11行目は、aタグをクリックした時に発生するイベントを1度全てキャンセルしています。キャンセルしておかないと、アンカーまで移動しちゃいます。
12行目は、アンカーリンクをクリックした時点のスクロール量を取得しています。これを用いて、アンカーまでの距離を計算します。
13行目は、クリックしたアンカーリンクのhrefを取得しています。
14行目は、13行目で取得したhrefを元にアンカーの要素を取得しています。
もしこの時に href の値の要素がなかった場合は、うまく動かないので、15行目でif文を入れています。
17行目は、16行目で取得した要素の絶対座標を元に、アンカー要素の絶対座標targetYを算出しています。
17行目では targetY に nowY を足して、18行目では nowYを引いているので、一見無駄なことをしているようですが、targetY は後述の繰り返しの時に必要な値なので、ここは気にしないでください。
18行目は1回ごとに進む最小のスクロール量をminYを出しています。
以上で出た値や、最初の設定値をdoScroll関数に渡しています。
doScroll関数
doScroll関数は divisor の数だけ繰り返し実行される関数です。
これが divisor回繰り返されることで、アンカーの要素までスクロールされます。一回の進み幅は minY です。
最初に定義されている toY はdoScroll関数が1回実行されるごとスクロールさせる絶対座標Yの位置です。
例えば1回目は、アンカーがアンカーリンクより下にある時は
toY = nowY1 + minY
です。
2回目は
toY = nowY2 + minY
となります。
ここで
nowY2 = nowY1 + minY
です。
ですので、最後のdivisor回目には
toY = nowY1 + minY×divisor ≒ targetY
となります。
doScroll関数の中で意外に困ったのが、setTimeoutで関数に値を渡すことでした。
intervalが2つあるので混乱しますが、
setTimeout( 's秒後に発火する関数' , s , 関数の引数 )
となっています。
意外にハマっちゃいました。
以上、スムーススクロールをjQueryなしで実装するでした。