スムーズスクロール

  • Updated: 2023.11.14
  • Published: 2023.02.09
  • 962views
  • GSAP
  • JavaScript
  • jQuery

概要

最近はアニメーションでGSAPをよく使用しているのですが、スムーズスクロールを実装させたい場合、jQueryのeaseScrollだとバッティングしているのか正常に動きません。
GSAPにもScrollSmootherというものがあるみたいですが有料のようで手が出ません。
というわけで自作してみました。
※当サイトで実装済み

ソース

export default class simpleSmoothScroll {
  constructor(option) {
    this.checkRequestAnimationFrame();

    this.option = {
      distance: 1,
      deceleration : 0.2
    };

    for(let prop in option){
      this.option[prop] = option[prop];
    }

    this.start();

    return this;
  }
  checkRequestAnimationFrame(){
    if ( !window.requestAnimationFrame ) {
      window.requestAnimationFrame = ( function() {
        return window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.oRequestAnimationFrame ||
          window.msRequestAnimationFrame ||
          window.requestAnimationFrame ||
          function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
            window.setTimeout( callback, 1000 / 60 );
          };
      } )();
      window.cancelAnimationFrame = ( function() {
        return window.webkitCancelAnimationFrame ||
          window.mozRequestAnimationFrame ||
          window.oCancelAnimationFrame ||
          window.msCancelAnimationFrame ||
          window.cancelAnimationFrame ||
          function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
            window.clearTimeout( callback );
          };
      } )();
    }
  }

  scrollTop(){
    return (window.pageYOffset || document.documentElement.scrollTop);
  }
  getStyle(element){
    return (element.currentStyle || document.defaultView.getComputedStyle(element, ''));
  }
  iframeScroll(flg){
    flg = flg || false;
    let iframe = document.querySelectorAll('iframe');
    iframe.forEach(($iframe)=>{
      if(flg){
        $iframe.style.pointerEvents = 'none';
      }else{
        $iframe.style.pointerEvents = 'auto';
      }
    });
  }
  start(){
    let self = this;
    let scrl = self.scrollTop();
    self.arrivalTop = scrl;
    let top = 100000000000;
    let scrtmr = {};
    let scrchk = {};
    let prevTop = 0;
    let count = 0;

    let $html = document.querySelector('html');
    let htmlStyle = self.getStyle($html);
    let htmlTop = parseInt(htmlStyle.marginTop);

    window.addEventListener('load',()=>{
      setTimeout(()=>{
        self.arrivalTop = self.scrollTop();
      },10);
    },false);

    document.addEventListener("DOMContentLoaded", ()=>{
      let anchor = document.querySelectorAll('a[href^="#"]');

      anchor.forEach(($anchor)=>{
        $anchor.addEventListener('click',(e)=>{
          e.preventDefault();
          count = 0;
          let href = e.currentTarget.getAttribute('href');
          self.scrollTo(href);
          return false;
        },false);
      });
    });

    window.addEventListener('wheel',(e)=>{
      let $dialog = document.querySelectorAll('dialog[open]');
      if($dialog.length > 0){
        return false;
      } else{
        if(e.preventDefault) e.preventDefault();
        if(e.stopPropagation) e.stopPropagation();
      }

      let y = e.deltaY;

      self.arrivalTop += y * self.option.distance;
      if(isNaN(self.arrivalTop)){
        self.arrivalTop = 0;
        scrl = 0;
      }
      top = document.querySelector('body').offsetHeight - window.innerHeight;
      if(self.arrivalTop < 0) self.arrivalTop = 0;
      else if(self.arrivalTop > top + htmlTop) self.arrivalTop = top + htmlTop;
      cancelAnimationFrame(scrtmr);
      scrtmr = requestAnimationFrame(step);
      cancelAnimationFrame(scrchk);
      self.iframeScroll(true);
    },{passive:false});

    window.addEventListener('touchend',(e)=>{
      scrl = self.arrivalTop = self.scrollTop();
    },{passive:false});
    window.addEventListener('keyup',(e)=>{
      scrl = self.arrivalTop = self.scrollTop();
    },{passive:false});

    window.addEventListener('scroll',(e)=>{
      scrl = self.scrollTop();
    },{passive:false});

    function step(){
      scrl += (self.arrivalTop - scrl) * self.option.deceleration;

      if(scrl < 0) scrl = 0;

      window.scrollTo(0,scrl);

      if(Math.abs(self.arrivalTop - scrl) < 2){
        window.scrollTo(0,self.arrivalTop);
        cancelAnimationFrame(scrtmr);
      }else{
        if(Math.abs(self.arrivalTop - scrl) < 20){
          self.iframeScroll(false);
        }
        scrtmr = requestAnimationFrame(step);
      }
    }

    self.scrollTo = (hash,margin)=>{
      margin = margin || 0;
      let $arv = document.querySelector(`${ hash }`);
      if($arv){
        prevTop = self.scrollTop();
        self.arrivalTop = $arv.getBoundingClientRect().top + self.scrollTop() - margin;
        scrtmr = requestAnimationFrame(step);
      }
    };

    ////
    function checkScroll(){
      if(prevTop === self.scrollTop()){
        count++;
        if(count > 5){
          cancelAnimationFrame(scrchk);
          scrl = self.arrivalTop = self.scrollTop();
        }else{
          scrchk = requestAnimationFrame(checkScroll);
        }
      }else{
        count = 0;
        scrchk = requestAnimationFrame(checkScroll);
      }
      prevTop = self.scrollTop();
    }
  }

}

使用方法

インポート

import simpleSmoothScroll from "./simpleSmoothScroll";

デフォルト

new simpleSmoothScroll();

減速率の設定

// default distance 1
// default deceleration 0.2
new simpleSmoothScroll({
  distance: 1.5,
  deceleration : 0.05
});

distance : マウスホイールの移動量

deceleration:減速率

Github

https://github.com/hiron712/simpleSmoothScroll

最後に

いかがでしたでしょうか?
ちなみにスムーズスクロールはPCのみ(wheelイベントが動作する時)で、タブレット、スマホはページ内スクロールのみ動作します。
実装サイトが当サイト以外でほぼ無い状態なので、不具合があればご連絡いただけると助かります。

関連記事

人気の投稿

最新の投稿

タグ

月別アーカイブ

Contact

WEB制作の依頼など気軽にお問い合わせください。

お問い合わせ