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

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

新規登録して質問してみよう
ただいま回答率
85.48%
スクロール

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

JavaScript

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

Q&A

解決済

1回答

4667閲覧

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

merry

総合スコア13

スクロール

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

JavaScript

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

0グッド

0クリップ

投稿2017/03/31 11:59

編集2017/03/31 15:17

タイマーで常に変数を監視し、慣性スクロールを疑似的に表現する自作の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;// スクロール先の座標 //////////////////////////////////////////////////// // このセクションでどういう処理をするのが良いか //////////////////////////////////////////////////// }

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

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; }

投稿2017/04/02 10:18

編集2017/04/02 10:23
merry

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問