jQueryを使用せずにスムーズにスクロール


83

プラグインやフレームワークの干渉なしに、UIに生のJavaScriptコードのみを使用したいページをコーディングしています。

そして今、私はjQueryなしでページをスムーズにスクロールする方法を見つけるのに苦労しています。


3
jQueryが行うのと同じ方法です。アニメーションを一連の非常に小さなステップに分割し、小さな間隔でインターバルタイマーを使用して、完了するまでこれらの各ステップを順番に実行します。
tvanfosson 2012

私は最初にこの解決策を考えましたが、私が見逃した唯一のポイントは、実際には以下のカマルのリンクにあります。これは、オブジェクトのY位置を計算する方法です。ありがとうtvanfosson :)


これは私の質問の1つに似ています。stackoverflow.com/questions/40598955/…をチェックしてください。
殺人剤2016年

回答:


27

このスムーズなスクロールデモ、または次のようなアルゴリズムを試してください。

  1. を使用して現在の上位の場所を取得します self.pageYOffset
  2. スクロールしたい場所までの要素の位置を取得します。 element.offsetTop
  3. forループを実行してそこに到達します。これは非常に高速です。または、タイマーを使用して、を使用してその位置までスムーズにスクロールします。 window.scrollTo

この質問に対する他の一般的な回答も参照してください。


Andrew Johnsonの元のコード:

function currentYPosition() {
    // Firefox, Chrome, Opera, Safari
    if (self.pageYOffset) return self.pageYOffset;
    // Internet Explorer 6 - standards mode
    if (document.documentElement && document.documentElement.scrollTop)
        return document.documentElement.scrollTop;
    // Internet Explorer 6, 7 and 8
    if (document.body.scrollTop) return document.body.scrollTop;
    return 0;
}


function elmYPosition(eID) {
    var elm = document.getElementById(eID);
    var y = elm.offsetTop;
    var node = elm;
    while (node.offsetParent && node.offsetParent != document.body) {
        node = node.offsetParent;
        y += node.offsetTop;
    } return y;
}


function smoothScroll(eID) {
    var startY = currentYPosition();
    var stopY = elmYPosition(eID);
    var distance = stopY > startY ? stopY - startY : startY - stopY;
    if (distance < 100) {
        scrollTo(0, stopY); return;
    }
    var speed = Math.round(distance / 100);
    if (speed >= 20) speed = 20;
    var step = Math.round(distance / 25);
    var leapY = stopY > startY ? startY + step : startY - step;
    var timer = 0;
    if (stopY > startY) {
        for ( var i=startY; i<stopY; i+=step ) {
            setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
            leapY += step; if (leapY > stopY) leapY = stopY; timer++;
        } return;
    }
    for ( var i=startY; i>stopY; i-=step ) {
        setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
        leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
    }
}

関連リンク:


2
良いアウトラインですが、タイムアウトのために実行する文字列を作成しないでください。先に進んで、実際の関数を呼び出す無名関数を作成してください。
tvanfosson 2012

@nnessそれは残念です。私は本当にコードを見たかったのです。
dzny 2014

39

JavaScriptでのネイティブブラウザのスムーズなスクロールは次のようになります。

// scroll to specific values,
// same as window.scroll() method.
// for scrolling a particular distance, use window.scrollBy().
window.scroll({
  top: 2500, 
  left: 0, 
  behavior: 'smooth' 
});

// scroll certain amounts from current position 
window.scrollBy({ 
  top: 100, // negative value acceptable
  left: 0, 
  behavior: 'smooth' 
});

// scroll to a certain element
document.querySelector('.hello').scrollIntoView({ 
  behavior: 'smooth' 
});

デモリンクを張ってもらえますか
DV Yogesh 2017年

2

2
@EvandroCavalcateSantos:もうありません!;-) developer.mozilla.org/en-US/docs/Web/API/Element/...
ペドロ・フェレイラ

1
この最新の標準は素晴らしく読みやすいですが、speedオプションが必要です。現在のスクロール速度は、フローするには遅すぎます。
ニルスリンデマン

31

編集:この回答は2013年に書かれました。requestAnimationFrameに関する以下のCristianTraìnaのコメントを確認してください

やった。以下のコードは、フレームワークに依存していません。

制限:アクティブなアンカーはURLに書き込まれません。

コードのバージョン:1.0 | Github:https//github.com/Yappli/smooth-scroll

(function() // Code in a function to create an isolate scope
{
var speed = 500;
var moving_frequency = 15; // Affects performance !
var links = document.getElementsByTagName('a');
var href;
for(var i=0; i<links.length; i++)
{   
    href = (links[i].attributes.href === undefined) ? null : links[i].attributes.href.nodeValue.toString();
    if(href !== null && href.length > 1 && href.substr(0, 1) == '#')
    {
        links[i].onclick = function()
        {
            var element;
            var href = this.attributes.href.nodeValue.toString();
            if(element = document.getElementById(href.substr(1)))
            {
                var hop_count = speed/moving_frequency
                var getScrollTopDocumentAtBegin = getScrollTopDocument();
                var gap = (getScrollTopElement(element) - getScrollTopDocumentAtBegin) / hop_count;

                for(var i = 1; i <= hop_count; i++)
                {
                    (function()
                    {
                        var hop_top_position = gap*i;
                        setTimeout(function(){  window.scrollTo(0, hop_top_position + getScrollTopDocumentAtBegin); }, moving_frequency*i);
                    })();
                }
            }

            return false;
        };
    }
}

var getScrollTopElement =  function (e)
{
    var top = 0;

    while (e.offsetParent != undefined && e.offsetParent != null)
    {
        top += e.offsetTop + (e.clientTop != null ? e.clientTop : 0);
        e = e.offsetParent;
    }

    return top;
};

var getScrollTopDocument = function()
{
    return document.documentElement.scrollTop + document.body.scrollTop;
};
})();

2
var _updateURL = function ( anchor, url ) { if ( (url === true || url === 'true') && history.pushState ) { history.pushState( {pos:anchor.id}, '', anchor ); } };ChrisFernandiのSmoothScroll jsを介して)のようなものを使用して、アンカーURLを履歴スタックにプッシュするだけでURLにアンカーを書き込むことができると思います。単に「JQueryを使用する」のではなく、これを行っている人々を見るのはうれしいです!:targetちなみにセレクターも優れたソリューションです。
Louis Maddox 2014

1
この回答は、パフォーマンスを向上させる既存のrequestAnimationFrameが既に記述されている2013年のものです。ここが使われない理由がわかりません。
クリスティアントレイニャ

24

アルゴリズム

要素をスクロールするには、scrollTop時間の経過とともにその値を変更する必要があります。特定の時点で、新しいscrollTop値を計算します。スムーズにアニメーション化するには、smooth-stepアルゴリズムを使用して補間します

scrollTop次のように計算します。

var point = smooth_step(start_time, end_time, now);
var scrollTop = Math.round(start_top + (distance * point));

どこ:

  • start_time アニメーションが開始された時間です。
  • end_timeアニメーションが終了するとき(start_time + duration)です;
  • start_topscrollTop最初の値です。そして
  • distanceは、目的の終了値と開始値の差です(target - start_top)

堅牢なソリューションは、アニメーションが中断されたときなどを検出する必要があります。詳細については、jQueryを使用しないスムーズスクロールに関する私の投稿を読んでください。

デモ

JSFiddleを参照してください

実装

コード:

/**
    Smoothly scroll element to the given target (element.scrollTop)
    for the given duration

    Returns a promise that's fulfilled when done, or rejected if
    interrupted
 */
var smooth_scroll_to = function(element, target, duration) {
    target = Math.round(target);
    duration = Math.round(duration);
    if (duration < 0) {
        return Promise.reject("bad duration");
    }
    if (duration === 0) {
        element.scrollTop = target;
        return Promise.resolve();
    }

    var start_time = Date.now();
    var end_time = start_time + duration;

    var start_top = element.scrollTop;
    var distance = target - start_top;

    // based on http://en.wikipedia.org/wiki/Smoothstep
    var smooth_step = function(start, end, point) {
        if(point <= start) { return 0; }
        if(point >= end) { return 1; }
        var x = (point - start) / (end - start); // interpolation
        return x*x*(3 - 2*x);
    }

    return new Promise(function(resolve, reject) {
        // This is to keep track of where the element's scrollTop is
        // supposed to be, based on what we're doing
        var previous_top = element.scrollTop;

        // This is like a think function from a game loop
        var scroll_frame = function() {
            if(element.scrollTop != previous_top) {
                reject("interrupted");
                return;
            }

            // set the scrollTop for this frame
            var now = Date.now();
            var point = smooth_step(start_time, end_time, now);
            var frameTop = Math.round(start_top + (distance * point));
            element.scrollTop = frameTop;

            // check if we're done!
            if(now >= end_time) {
                resolve();
                return;
            }

            // If we were supposed to scroll but didn't, then we
            // probably hit the limit, so consider it done; not
            // interrupted.
            if(element.scrollTop === previous_top
                && element.scrollTop !== frameTop) {
                resolve();
                return;
            }
            previous_top = element.scrollTop;

            // schedule next frame for execution
            setTimeout(scroll_frame, 0);
        }

        // boostrap the animation process
        setTimeout(scroll_frame, 0);
    });
}

これは本当に良い解決策のようです。しかし、Fiddleにあるような要素ではなく、ウィンドウオブジェクトにどのように実装できますか?
mudlabs 2016

私がそれを理解したことを気にしないでください。の代わりにpageYOffsetとを使用する必要がありscrollTo()ました。elementscrollTop
mudlabs 2016

6

ここでjQueryなしの例を作成しました:http//codepen.io/sorinnn/pen/ovzdq

/**
    by Nemes Ioan Sorin - not an jQuery big fan 
    therefore this script is for those who love the old clean coding style  
    @id = the id of the element who need to bring  into view

    Note : this demo scrolls about 12.700 pixels from Link1 to Link3
*/
(function()
{
      window.setTimeout = window.setTimeout; //
})();

      var smoothScr = {
      iterr : 30, // set timeout miliseconds ..decreased with 1ms for each iteration
        tm : null, //timeout local variable
      stopShow: function()
      {
        clearTimeout(this.tm); // stopp the timeout
        this.iterr = 30; // reset milisec iterator to original value
      },
      getRealTop : function (el) // helper function instead of jQuery
      {
        var elm = el; 
        var realTop = 0;
        do
        {
          realTop += elm.offsetTop;
          elm = elm.offsetParent;
        }
        while(elm);
        return realTop;
      },
      getPageScroll : function()  // helper function instead of jQuery
      {
        var pgYoff = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
        return pgYoff;
      },
      anim : function (id) // the main func
      {
        this.stopShow(); // for click on another button or link
        var eOff, pOff, tOff, scrVal, pos, dir, step;

        eOff = document.getElementById(id).offsetTop; // element offsetTop

        tOff =  this.getRealTop(document.getElementById(id).parentNode); // terminus point 

        pOff = this.getPageScroll(); // page offsetTop

        if (pOff === null || isNaN(pOff) || pOff === 'undefined') pOff = 0;

        scrVal = eOff - pOff; // actual scroll value;

        if (scrVal > tOff) 
        {
          pos = (eOff - tOff - pOff); 
          dir = 1;
        }
        if (scrVal < tOff)
        {
          pos = (pOff + tOff) - eOff;
          dir = -1; 
        }
        if(scrVal !== tOff) 
        {
          step = ~~((pos / 4) +1) * dir;

          if(this.iterr > 1) this.iterr -= 1; 
          else this.itter = 0; // decrease the timeout timer value but not below 0
          window.scrollBy(0, step);
          this.tm = window.setTimeout(function()
          {
             smoothScr.anim(id);  
          }, this.iterr); 
        }  
        if(scrVal === tOff) 
        { 
          this.stopShow(); // reset function values
          return;
        }
    }
 }

1
関連するコードの抜粋またはその仕組みの説明をここに投稿してください。あなたの答えは、私が何もクリックしなくても意味をなすことができるはずです。ありがとうございました!
エドワード

おかげで-しかしいいえ-codeopenは例を共有するために作られています-ここはすべてのcss / js / htmlコードを置くのに適切な場所ではありません-utilstackoverflowは必要なビットを追加します-ほとんどの人はあなたをcodepen / jsfiddle /または他の場所に送ります..
SorinN 2014年

1
これが私たちがそれを求める理由についての会話の一部です:meta.stackexchange.com/q/149890
エドワード

2
私はその必要性を理解していませんwindow.setTimeout = window.setTimeout;
pmrotule 2017年

6

最新のブラウザは、CSSの「scroll-behavior:smooth」プロパティをサポートしています。したがって、このためにJavascriptはまったく必要ありません。これをbody要素に追加し、通常のアンカーとリンクを使用するだけです。 スクロール動作MDNドキュメント


EdgeとSafariはどちらもこのプロパティをまだサポートしていません。私は彼らがすぐにそうなることを願っています。
67545343 6719

Firefoxでもうまく機能しません。したがって、それは解決策ではありません。
huseyin 3919

5

私は最近、jQueryがオプションではなかった状況でこの問題を解決しようと試みたので、後世のためにここに解決策を記録しています。

var scroll = (function() {

    var elementPosition = function(a) {
        return function() {
            return a.getBoundingClientRect().top;
        };
    };

    var scrolling = function( elementID ) {

        var el = document.getElementById( elementID ),
            elPos = elementPosition( el ),
            duration = 400,
            increment = Math.round( Math.abs( elPos() )/40 ),
            time = Math.round( duration/increment ),
            prev = 0,
            E;

        function scroller() {
            E = elPos();

            if (E === prev) {
                return;
            } else {
                prev = E;
            }

            increment = (E > -20 && E < 20) ? ((E > - 5 && E < 5) ? 1 : 5) : increment;

            if (E > 1 || E < -1) {

                if (E < 0) {
                    window.scrollBy( 0,-increment );
                } else {
                    window.scrollBy( 0,increment );
                }

                setTimeout(scroller, time);

            } else {

                el.scrollTo( 0,0 );

            }
        }

        scroller();
    };

    return {
        To: scrolling
    }

})();

/* usage */
scroll.To('elementID');

このscroll()関数は、Revealing Module Patternを使用してscrolling()、を介してターゲット要素のIDを関数に渡しscroll.To('id')ます。これにより、によって使用される値が設定されます。scroller()関数でます。

壊す

scrolling()

  • el :ターゲットDOMオブジェクト
  • elPos :を介して関数を返します elememtPosition()呼び出されるたびに、ページの上部を基準にしたターゲット要素の位置を指定。
  • duration :ミリ秒単位の遷移時間。
  • increment :ターゲット要素の開始位置を40ステップに分割します。
  • time :各ステップのタイミングを設定します。
  • prev:ターゲット要素の前の位置scroller()
  • E:ターゲット要素の位置をscroller()。に保持します。

実際の作業は、ターゲット要素がページの上部に来るか、ページがスクロールできなくなるまで、scroller()(を介してsetTimeout())自身を呼び出し続ける関数によって行われます。

scroller()呼び出されるたびに、ターゲット要素(変数に保持されているE)の現在の位置がチェックされ、それが> 1OR< -1であるかどうか、ページがまだスクロール可能であるかどうかは、ウィンドウが正または負の値であるかincrementどうかに応じてピクセル単位で上下にシフトしますEE> 1ORでも< -1E===prevでもない場合、関数は停止します。追加しましたDOMElement.scrollTo()ターゲット要素がウィンドウの上部で強打されていることを確認するために、完了時にメソッド(ピクセルの何分の1かで外れていることに気付かないでください!)。

if2行目のステートメントscroller()E、前の位置(prev)と照合して、ページがスクロールしているかどうかを確認します(ターゲットがページの下部に向かっていて、ページがそれ以上スクロールできない場合)。

その下の3値条件は、ゼロに近づくにつれてincrement値を減らしEます。これにより、ページが一方の方向にオーバーシュートし、次に跳ね返ってもう一方の方向にオーバーシュートし、次に跳ね返ってもう一方の方向にオーバーシュートし、ピンポンスタイルで無限のかなたになります。

ページの高さが約4000pxを超える場合は、3項式の最初の条件(ここでは+/- 20)および/またはincrement値を設定する除数(ここでは40)の値を増やすことができます。

約遊ぶdurationセット除数、incrementとの三元状態での値scroller()ページに合うように調整する機能を、あなたを可能にしなければなりません。

  • JSFiddle

  • NBLubuntuではFirefoxとChromeの最新バージョンで、Windows8ではFirefox、Chrome、IEでテスト済み。



2

私はこのようなものを作りました。IE8で動作するかどうかはわかりません。IE9、Mozilla、Chrome、Edgeでテスト済み。

function scroll(toElement, speed) {
  var windowObject = window;
  var windowPos = windowObject.pageYOffset;
  var pointer = toElement.getAttribute('href').slice(1);
  var elem = document.getElementById(pointer);
  var elemOffset = elem.offsetTop;

  var counter = setInterval(function() {
    windowPos;

    if (windowPos > elemOffset) { // from bottom to top
      windowObject.scrollTo(0, windowPos);
      windowPos -= speed;

      if (windowPos <= elemOffset) { // scrolling until elemOffset is higher than scrollbar position, cancel interval and set scrollbar to element position
        clearInterval(counter);
        windowObject.scrollTo(0, elemOffset);
      }
    } else { // from top to bottom
      windowObject.scrollTo(0, windowPos);
      windowPos += speed;

      if (windowPos >= elemOffset) { // scroll until scrollbar is lower than element, cancel interval and set scrollbar to element position
        clearInterval(counter);
        windowObject.scrollTo(0, elemOffset);
      }
    }

  }, 1);
}

//call example

var navPointer = document.getElementsByClassName('nav__anchor');

for (i = 0; i < navPointer.length; i++) {
  navPointer[i].addEventListener('click', function(e) {
    scroll(this, 18);
    e.preventDefault();
  });
}

説明

  • pointer-属性が「href」の場合は要素とchceckを取得し、「#」の場合は削除します。
  • elem-「#」のないポインタ変数
  • elemOffset-ページ上部からの「スクロール先」要素のオフセット

2

使用できます

document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});

ページの上部を上にスクロールする場合は、空の要素を上部に配置して、その要素までスムーズにスクロールできます。


0

window.scrollToとsetTimeoutでforループを使用して、プレーンなJavascriptでスムーズにスクロールできます。私のscrollToSmoothly関数で要素にスクロールするには:scrollToSmoothly(elem.offsetTop)elemDOM要素であると仮定します)。これを使用して、ドキュメント内の任意のy位置にスムーズにスクロールできます。

function scrollToSmoothly(pos, time){
/*Time is only applicable for scrolling upwards*/
/*Code written by hev1*/
/*pos is the y-position to scroll to (in pixels)*/
     if(isNaN(pos)){
      throw "Position must be a number";
     }
     if(pos<0){
     throw "Position can not be negative";
     }
    var currentPos = window.scrollY||window.screenTop;
    if(currentPos<pos){
    var t = 10;
       for(let i = currentPos; i <= pos; i+=10){
       t+=10;
        setTimeout(function(){
        window.scrollTo(0, i);
        }, t/2);
      }
    } else {
    time = time || 2;
       var i = currentPos;
       var x;
      x = setInterval(function(){
         window.scrollTo(0, i);
         i -= 10;
         if(i<=pos){
          clearInterval(x);
         }
     }, time);
      }
}

デモ:


0
<script>
var set = 0;

function animatescroll(x, y) {
    if (set == 0) {
        var val72 = 0;
        var val73 = 0;
        var setin = 0;
        set = 1;

        var interval = setInterval(function() {
            if (setin == 0) {
                val72++;
                val73 += x / 1000;
                if (val72 == 1000) {
                    val73 = 0;
                    interval = clearInterval(interval);
                }
                document.getElementById(y).scrollTop = val73;
            }
        }, 1);
    }
}
</script>

x = scrollTop
y =スクロールに使用されるdivのID

注: 本体をスクロールさせるには、本体にIDを指定します。


0

これが私の解決策です。ほとんどのブラウザで動作します

document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});

ドキュメント

document.getElementById("end").scrollIntoView({behavior: "smooth"});
body {margin: 0px; display: block; height: 100%; background-image: linear-gradient(red, yellow);}
.start {display: block; margin: 100px 10px 1000px 0px;}
.end {display: block; margin: 0px 0px 100px 0px;}
<div class="start">Start</div>
<div class="end" id="end">End</div>



-1

これが私のために働いたコードです。

`$('a[href*="#"]')

    .not('[href="#"]')
    .not('[href="#0"]')
    .click(function(event) {
     if (
       location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
       &&
       location.hostname == this.hostname
        ) {

  var target = $(this.hash);
  target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
  if (target.length) {

    event.preventDefault();
    $('html, body').animate({
      scrollTop: target.offset().top
    }, 1000, function() {

      var $target = $(target);
      $target.focus();
      if ($target.is(":focus")) { 
        return false;
      } else {
        $target.attr('tabindex','-1'); 
        $target.focus(); 
      };
    });
    }
   }
  });

`


コードの機能についてもう少し説明していただけますか?
rhavelka

1
stackoverflowへようこそ。回答していただきありがとうございますが、質問はjqueryを使用しないことに関するものでした。
ステファン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.