Home > 備忘録 > javascript/jQuery > スムーズスクロールについて考える

スムーズスクロールについて無駄に考えてみたメモ

やってみたいことは、ムーズスクロールと「ページ上部へ戻る」リンクをフェードインで表示させる。です。
単純ですが、ハッシュの更新にこだわっていたのでこの2個の共存がちょっと面倒でした(´・ω・)

ページが縦に長かったりFAQのように細かい項目がたくさんあるページでは折角ページ内リンクがあってもハッシュが更新されてないと、リンクをしたり他の人にURLを伝える時めんどくさいのでURLのハッシュを更新するのははずせないところ。

あえて更新させたくないという場合もありますが稀なケースだと思うので今回は考えない方向で。

というわけで、以下の条件の組み合わせを実装してみようと思います。

・スムーズスクロールする
・URLのハッシュを更新する
・スクロールに合わせてトップに戻るリンクをフェードインで表示する

Headline
  • ページトップへ戻るリンクのアニメーション
  • Test1 アニメーションでIDにジャンプさせる
  • Test2 location.hashでハッシュを更新するようにしてみた
  • Test3 コールバックでlocation.hashを更新してみた
  • Test4 location.hashで更新したあと位置を戻してみた
  • Test5 IDを一時的に削除してみた
  • Test6 IEの事は忘れてみた

ページトップへ戻るリンクのアニメーション

とりあえずトップへ戻るリンクにスクロール量に依存したアニメーションをつけます。

$(window).scroll(function () {
        if ($(this).scrollTop() > 200) {
                $('#PageTopLink').not(":animated").fadeIn();
        } else {
                $('#PageTopLink').not(":animated").fadeOut();
        }
});
$(document).ready(function(){
        $('#PageTopLink').hide();
});

ついでに、イージングの「easeOutQuart」だけ登録しておきます。

$.extend($.easing, {
        easeOutQuart: function(x, t, b, c, d){
                return -c * ((t = t / d - 1) * t * t * t - 1) + b;
        }
});

Test1 アニメーションでIDにジャンプさせる

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                event.preventDefault();
                if ($(this.hash).length) {
                        var position = $(this.hash).offset().top;
                        $('html,body').animate({
                                scrollTop: position
                        }, 1000, 'easeOutQuart');
                }
        }
});

テストページ

すごくオーソドックスなタイプのスクロールです。
余計なことをしていないのでスクロールの動きは古いPCを使っていても多分これが一番スムーズかなぁと思います。
でもハッシュが更新されないのでURLのコピペが出来ないのが難点…。

Test2 location.hashでハッシュを更新するようにしてみた

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                event.preventDefault();
                var hash = this.hash.replace(/^#/, '');
                if ($('#' + hash).length) {
                        var position = $('#' + hash).offset().top;
                        $('html,body').animate({
                                scrollTop: position
                        }, 1000, 'easeOutQuart');
                        location.hash = hash;
                }
        }
});

テストページ

location.hashを加えてハッシュを更新するようにしてみました。(またはevent.preventDefault()を使わない)
タイミングで2回分アニメーションが実行されるのと、Firefoxでは問題ないのですがChromeとIEだとスクロールバーがブルっててちょっと気持ち悪い。
スクロールのアニメーション中にハッシュが更新されるためかなと思われます。

Test3 コールバックでlocation.hashを更新してみた

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                event.preventDefault();
                var hash = this.hash.replace(/^#/, '');
                if ($('#' + hash).length) {
                        var position = $('#' + hash).offset().top;
                        $('html,body').animate({
                                scrollTop: position
                        }, 1000, 'easeOutQuart', function(){
                                location.hash = hash;
                        });
                }
        }
});

テストページ

それならばスクロールのアニメーションで移動し終わったあとにlocation.hashでハッシュを更新してみました。
結構期待通りの動きになってきました。
が、ブラウザの戻るを押したときに直前居た位置に戻れないというのが難点かな・・・。
ブラウザの移動を使っているとこれが割りと不便です。

Test4 location.hashで更新したあと位置を戻してみた

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                event.preventDefault();
                var hash = this.hash.replace(/^#/, '');
                if ($('#' + hash).length) {
                        var tmptop = $(window).scrollTop();
                        location.hash = hash;
                        var position = $('#' + hash).offset().top;
                        $('html,body').scrollTop(tmptop).animate({
                                scrollTop: position
                        }, 1000, 'easeOutQuart');
                }
        }
});

テストページ

ハッシュを更新したあとに再度現在の位置を指定したらどうかな。
ハッシュの更新で飛んだ後に→現在の位置に戻っる この処理にスクロールバーがカクついたりとか見た目的な違和感があるかなー…と思いながら試してみたのですが意外と違和感はありませんでした。

一旦飛んだ後に戻るとか処理上のモヤッと感はありますが(´・ω・`) 表面上問題ないなら今のところこれがベストかな…。

Test5 IDを一時的に削除してみた

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                event.preventDefault();
                var hash = this.hash.replace(/^#/, '');
                var el = $('#' + hash);
                if (el.length) {
                        el.removeAttr('id');
                        location.hash = hash;
                        el.attr('id', hash);
                        var position = el.offset().top;
                        $('html,body').animate({
                                scrollTop : position
                        }, 1000, 'easeOutQuart');
                }
        }
});

テストページ

location.hashでジャンプしないのは「IDが存在しない時」というわけなので、一時的にIDを削除してみました。
削除されるのはハッシュを更新する一瞬ですが、そのID自体を使って何か処理をしている途中とかだとなにか不具合が出そうな気も。

Test6 IEの事は忘れてみた

$(document).on('click', 'a[href*=#]', function(event){
        if (this.href.indexOf("#") === 0 || (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname)) {
                if (typeof(window.history.pushState) == 'function') {
                        event.preventDefault();
                        var hash = this.hash.replace(/^#/, '');
                        if ($('#' + hash).length) {
                                var position = $('#' + hash).offset().top;
                                $('html,body').animate({
                                        scrollTop: position
                                }, 1000, 'easeOutQuart');
                                history.pushState(null, null, location.pathname + location.search + '#' + hash);
                        }
                }
        }
});

テストページ

pushState対応ブラウザのみに絞ってみました。
プロモーションサイトとかでクロスブラウザで拘りたいっていう時以外はこれでもよさそう。

ちなみに、Safariでは確認していないのでうごかないよって場合はご連絡ください。(o_ _)o


Edited 2015.06.16 Created 2013.01.29 JavascriptjQueryWebデザイン

PAGETOP