質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • JavaScript

    20334questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • スクロール

    35questions

    スクロールとは、ディスプレイスクリーン上において連続的にコンテンツが滑っていくことを指します。

【非jQuery】疑似慣性スクロール機能のあるJavaScriptでページ内移動を実装したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,908

merry

score 4

タイマーで常に変数を監視し、慣性スクロールを疑似的に表現する自作のJSがあります。
iphone safariでは標準スクロール中にアニメーションのレンダリングが止まる問題があり、
それに対処するためにcssのtopプロパティにマイナスの値を入れることで疑似的にスクロールしているように見せています。
jqueryやその他フレームワークには依存せずネイティブで書いています。

そこで「ページトップへ戻る」ボタンのようにページ内スクロールをしたいのですが、
うまい実装方法があれば教えていただきたいです。
トップだけではなく特定のセクションへも移動できる必要があります。

ブラウザ標準スクロールと疑似スクロールを切り替える機能があり、
標準スクロールモードなら普通にjqueryなどで楽にページ内移動できます。
ただ慣性スクロールをしたい場合にどうしたらいいのか客観的に見られなくなってきて悩んでいます。

全コードは長くなるので割愛しますが下記が当問題にかかわる部分です。

以上質問の仕方が難しいですがよろしくお願いいたします。

画面描画・監視用のメソッド

render : function render() {
                // アニメーションをレンダリングするメソッド
                var self = this;
                var getPos = self.getPos();
                render.speed = render.speed || 0;

                // 初めて呼び出した場合スタイルを設定
                if (!body.style.top  && Merimiroll.touch_mode === true) {
                    Merimiroll.scroll.y = 0;
                    body.style.cssText = "position: fixed; " + "top:" + 0 + "px;";

                    // ウィンドウをfixedにしてスクロールバーが消えたのでサイズを再取得
                    self.updateSize();
                }

                if (!render.setListener) {
                    // 初回呼び出し時にタッチムーブイベントをセット
                    this.addEvent(window, "touchmove", function (e) {
                        self.emulateScroll(e);
                    });
                    this.addEvent(window, "resize", function (e) {
                        self.updateSize();
                    });
                    if (Merimiroll.touch_mode === true) {
                        this.addEvent(window, "wheel", function (e) {
                            self.emulateScroll(e);
                        });
                    }

                    render.setListener = true;
                }

                // ナビゲーションフラグが立っている場合 イベントリスナが登録されたボタンが押されるとisNavigationをtrueにする
                if (Merimiroll.isNavigation) {
                    /////////////////////////////////////////////

          // このセクション内をどうするか知りたい
                    Merimiroll.dy = Math.floor(Merimiroll.scroll.targetY - Merimiroll.scroll.y); // targetY をうまく更新できたらいいような気がする
                    Merimiroll.vy += Merimiroll.dy;
                    Merimiroll.vy *= Merimiroll.friction;// 慣性スクロールのための摩擦
                    Merimiroll.scroll.y += Merimiroll.vy;
                    Merimiroll.scroll.targetY = Merimiroll.scroll.y;

                    /////////////////////////////////////////////
                } else {
                    // このセクションはちゃんと動く
                    Merimiroll.dy = Math.floor(Merimiroll.scroll.targetY - Merimiroll.scroll.y);

                    Merimiroll.vy += Merimiroll.dy;
                    Merimiroll.vy *= Merimiroll.friction;// 慣性スクロールのための摩擦
                    Merimiroll.scroll.y += Merimiroll.vy;
                    Merimiroll.scroll.targetY = Merimiroll.scroll.y;

                    Merimiroll.isNavigation = false;
                }



                // 全体のスクロール位置を更新
                body.style.cssText = "position: fixed; " + "top:" + Math.floor(Merimiroll.scroll.y) + "px;";

                // スクロールバーの位置
                scrollbar.amount_move = window_height * (Merimiroll.scroll.y / container_height);
                scrollbar.el.style.cssText += "position: fixed; " + "top:" + -scrollbar.amount_move + "px;";

                // 一番下までスクロールした時
                if (Merimiroll.scroll.y < -distance_max) {
                    body.style.cssText = "position: fixed; " + "top:" + -distance_max + "px;";
                    Merimiroll.vy = 0;// 速度をなくす
                    // スクロールバー
                    scrollbar.el.style.cssText += "position: fixed; " + "top:" + -distance_max + "px;";
                    Merimiroll.scroll.y = -distance_max;
                }
                // 一番上までスクロールした時
                if (Merimiroll.scroll.y > 0) {
                    Merimiroll.scroll.y = 0;
                    body.style.cssText = "position: fixed; " + "top:" + 0 + "px;";
                    Merimiroll.vy = 0;// 速度をなくす

                    // スクロールバー
                    scrollbar.el.style.cssText += "position: fixed; " + "top:" + 0 + "px;";
                }

                Merimiroll.timerId = function(){
                    Merimiroll.prototype.render();
                };

                setTimeout(Merimiroll.timerId, 60);// 更新
            }
emulateScroll : function _emulateScroll(e) {
                e =  e || window.event; //for legacy IE
                e.preventDefault();// デフォルトのスクロール動作をキャンセル
                e.cancelBubble = true;
                e.stopPropagation();
                var self = this;


                // ここからイベントの処理
                if (!_emulateScroll.setListener) {
                    this.addEvent(window, "touchstart", function() {
                        self.touchStart();
                    });

                    _emulateScroll.setListener = true;
                }

                if (e.type == "touchmove") {

                    // 割愛 ///////////////////////

                } else if (e.type == "wheel") {
                    Merimiroll.clock = e.timeStamp - Merimiroll.save;
                    Merimiroll.save = e.timeStamp;

                    if (Merimiroll.clock < Merimiroll.clock_limit) {
                        return false;// 比較結果が指定ミリ秒より少ない場合はキャンセル
                    }

                        // pcでスクロールされた時
                    if (this.getDelta(e) < 0 && Merimiroll.scroll.y > -distance_max) {
                        // 下にスクロールした場合の処理
                        Merimiroll.scroll.targetY = (Merimiroll.scroll.y + -Merimiroll.speed);
                    } else if (this.getDelta(e) > 0) {
                        // 上にスクロールした場合の処理
                        if (Merimiroll.scroll.y >= -distance_max && Merimiroll.scroll.y !== 0) {
                            Merimiroll.scroll.targetY = (Merimiroll.scroll.y + Merimiroll.speed);
                        }
                    }
                }
            }

トップへ戻るボタンへ登録するイベントハンドラ

hrefから#hogeを取得しその要素(移動先)の座標を取得

scrollTo: function _scrollTo (e) {
                if (Merimiroll.isNavigation == true) {
                    return false;// 移動中はフラグをはずして何度も押せなくする
                }
                e = e || window.event;
                var reg = new RegExp("(#{1})([\\w\\-\\_]*)");// 先頭が#記号かつ、-と_を含んだ文字列を取得
                var href = e.target.getAttribute("href");
                _scrollTo.link_target = href.replace(reg, "$2");// #記号を削除
                var self = this;
                var nav = e.target;
                var target_view = document.getElementById(_scrollTo.link_target) ? document.getElementById(_scrollTo.link_target) : null;
                // スクロール値を取得
                var scrollTop = Math.floor(Merimiroll.prototype.getPos().y);
                var x = target_view.getBoundingClientRect().left;
                var y = target_view.getBoundingClientRect().top;// スクロール先の座標
                var viewY = Math.floor(scrollTop + y);// ドキュメント座標
                _scrollTo.distance = Math.floor(scrollTop - viewY);

                Merimiroll.isNavigation = true;
                Merimiroll.navigation_distance = _scrollTo.distance;
                Merimiroll.navigation_distance_now = Merimiroll.navigation_distance;
                Merimiroll.navigation_view = target_view;
                Merimiroll.navigation_view_docY = viewY;
                Merimiroll.navigation_view_x = target_view.getBoundingClientRect().left;
                Merimiroll.navigation_view_y = target_view.getBoundingClientRect().top;// スクロール先の座標


                ////////////////////////////////////////////////////

                // このセクションでどういう処理をするのが良いか

                ////////////////////////////////////////////////////


            }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

自己解決

friction = 0.9 だった部分の減衰を 0.25に強くし、
目標地点に一定距離まで近づいたら速度vyを0にしました。
上記の距離になった時に目標へ到達したとみなし、
スクロール位置を目標位置の値に設定しました。

               if (Merimiroll.isNavigation == true) {
                    Merimiroll.scroll.targetY = -Merimiroll.navigation_view_docY;
                    Merimiroll.dy = Math.floor(Merimiroll.scroll.targetY - Merimiroll.scroll.y);
                    Merimiroll.ay = Merimiroll.dy * Merimiroll.spring;// 加速度を求める
                    Merimiroll.vy += Merimiroll.ay;
                    Merimiroll.vy *= 0.25;// 慣性スクロールのための摩擦 これが大きいとバネ運動になってしまう
                    Merimiroll.scroll.y += Math.floor(Merimiroll.vy);

                    if (Math.abs(Merimiroll.scroll.targetY - Merimiroll.scroll.y) < 10) {
                        Merimiroll.scroll.y = Math.floor(Merimiroll.scroll.targetY);
                        Merimiroll.vy = 0;
                        Merimiroll.isNavigation = false;
                        Merimiroll.scroll.targetY = Merimiroll.scroll.y;
                    }

                }
scrollTo: function _scrollTo (e) {
                if (Merimiroll.isNavigation == true) {
                    return false;// 移動中はフラグをはずして何度も押せなくする
                }
                e = e || window.event;
                var self = this;
                var reg = new RegExp("(#{1})([\\w\\-\\_]*)");// 先頭が#記号かつ、-と_を含んだ文字列を取得する正規表現
                var href = e.target.getAttribute("href");
                _scrollTo.link_target = href.replace(reg, "$2");// #記号を削除
                var target_view;

                if (_scrollTo.link_target !== "") {
                    // IDが存在するのでターゲットにする
                    target_view = document.getElementById(_scrollTo.link_target);
                } else {
                    // href="#" などリンク先がIDでない場合、
                    // #が削除されて空文字なのでbodyをターゲットにする
                    target_view = body;
                }

                // スクロール値を取得
                var scrollTop = Math.floor(Merimiroll.prototype.getPos().y);
                var x = target_view.getBoundingClientRect().left;
                var y = target_view.getBoundingClientRect().top;// スクロール先の座標
                var viewY = Math.floor(y - scrollTop);// ドキュメント座標

                Merimiroll.isNavigation = true;
                Merimiroll.navigation_view = target_view;
                Merimiroll.navigation_view_docY = viewY;
            }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • JavaScript

    20334questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • スクロール

    35questions

    スクロールとは、ディスプレイスクリーン上において連続的にコンテンツが滑っていくことを指します。