こんにちは、ノベルティの山本です。
今回は、簡単にWindowsのスクロールをMacの様に滑らかにするコードをご紹介したいと思います。

はじめに

これからご紹介するコードは万能のコードではありません。
最初にbodyタグの高さを取得して擬似的にスムーススクロールさせているため、jqueryなどのアコーディオンのアニメーションをはじめとした高さが変わる系のイベントを使用している場合は、高さが変動した分に応じて、フッターが切れたり、余白が生まれたりするのでご注意を。
要素の高さが可変するイベントの発火時にスムーススクロールを逐一呼び出せば問題ないと思われます。

また、デバイス検出で条件分岐して、アップル製品のデバイスではコードが適用されない様にしましょう。

ちなみに最新バージョンのIE、Edgeでも問題なく動きますが、ビルドツール・コンパイルツールを通さないでコードを使用する場合は、エラー吐く可能性もなきにしもあらずなのでバベルを使ってトランスパイルしてから使用してください。

サンプルコード

const math = {
    lerp: (a, b, n) => {
        return (1 - n) * a + n * b;
    },
    norm: (value, min, max) => {
        return (value - min) / (max - min);
    }
};
class Smooth {
    constructor() {
        this.bindMethods();
        this.data = {
            ease: 0.1,
            current: 0.1,
            last: 0.1
        };
        this.dom = {
            el: document.getElementById('wrapper'),
            content: document.getElementById('inner')
        };
        this.rAF = null;
        this.init();
    }
    bindMethods() {
        ['scroll', 'run', 'resize'].
        forEach(fn => this[fn] = this[fn].bind(this));
    }
    setStyles() {
        Object.assign(this.dom.content.style, {
            position: 'fixed',
            width: '100%',
            overflow: 'hidden'
        });
    }
    setHeight() {
        document.body.style.height = `${this.dom.content.offsetHeight}px`;
    }
    resize() {
        this.setHeight();
        this.scroll();
    }
    preload() {
        imagesLoaded(this.dom.content, (instance) => {
            this.setHeight();
        });
    }
    scroll() {
        this.data.current = window.pageYOffset;
    }
    run() {
        this.data.last = math.lerp(this.data.last, this.data.current, this.data.ease);
        if (this.data.last < 0.1) {
            this.data.last = 0;
        }
        this.dom.content.style.transform = `translate3d(0, -${this.data.last}px, 0)`;
        this.requestAnimationFrame();
    }
    on() {
        this.setStyles();
        this.setHeight();
        this.addEvents();
        this.requestAnimationFrame();
    }
    off() {
        this.cancelAnimationFrame();
        this.removeEvents();
    }
    requestAnimationFrame() {
        this.rAF = requestAnimationFrame(this.run);
    }
    cancelAnimationFrame() {
        cancelAnimationFrame(this.rAF);
    }
    destroy() {
        document.body.style.height = '';
        this.data = null;
        this.removeEvents();
        this.cancelAnimationFrame();
    }
    resize() {
        this.setHeight();
    }
    addEvents() {
        window.addEventListener('resize', this.resize, { passive: true });
        window.addEventListener('scroll', this.scroll, { passive: true });
    }
    removeEvents() {
        window.removeEventListener('resize', this.resize, { passive: true });
        window.removeEventListener('scroll', this.scroll, { passive: true });
    }
    init() {
        this.preload();
        this.on();
    }
}
new Smooth();

使い方

17行目の下記コードに設定したい要素を入れてください。
elの方が名前の通りラッパーになっていて、contentの方がスクロールする中身が全て詰まった要素になります。

this.dom = {
    el: document.getElementById('wrapper'),
    content: document.getElementById('inner')
};

このコードを使用する場合は、mainタグの中にfooterタグを入れる必要が出てきます。イメージとしてはこんな感じです。

<body>
    <header></header> //通常の様にfixedさせたい場合、#wrapperの外に出さないと挙動がおかしくなります
    <div id="wrapper"> //#innerの支点になる
        <div id="inner"> //position: fixed;になる部分
            <main>
                <footer></footer>
            </main>
        </div>
    </div>
</body>

スームーススクロールになる仕組み

スムーススクロールになる仕組みとしては、#wrapperを支点として#inner部分がposition: fixed;になりスクロール量に合わせてtransform: translate3d(x, y, z)を利用しコンテンツをスクロールさせています。
サンプルコードの12行目を修正することで、transform: translate3d(x, y, z)のスペードを変化させることが出来ます。

this.data = {
    ease: 0.1,
    current: 0.1,
    last: 0.1
};

ソースコード

ソースコードは確か、コードロップこの記事から引っ張ってきたかと思います。
コードドロップは、WEB制作に使える最新のデザインや情報がたくさん詰まっているのでとても便利なサイトだと思います。
部分で使えるような色々なアニメーションがほんとにたくさんあるので、リッチな表現をしたいときにとても参考になると思います。

PAGE TOP