jQuery를 사용하지 않고 부드러운 스크롤


83

플러그인이나 프레임 워크의 간섭없이 UI에 원시 JavaScript 코드 만 사용하려는 페이지를 코딩하고 있습니다.

그리고 이제 jQuery없이 페이지를 부드럽게 스크롤하는 방법을 찾는 데 어려움을 겪고 있습니다.


3
jQuery와 같은 방식입니다. 애니메이션을 일련의 매우 작은 단계로 나누고 작은 간격으로 간격 타이머를 사용하여 완료 될 때까지 각 단계를 차례로 수행합니다.
tvanfosson

나는 처음에이 솔루션을 생각했고 내가 놓친 유일한 점은 실제로 물체의 Y 위치를 계산하는 방법 인 아래 Kamal의 링크에 있습니다. 감사합니다 tvanfosson :)


이것은 내 질문 중 하나와 비슷합니다. stackoverflow.com/questions/40598955/…를 확인하십시오 .
Cannicide

답변:


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

@nness 그것은 부끄러운 일입니다. 나는 정말로 코드를보고 싶었다.
dzny

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

2
에 설명 된대로 @Vishu라나, 행동 지원은 파이어 폭스로 이동 MDN
Evandro Cavalcate 산토스에게

2
@EvandroCavalcateSantos : 더 이상은 아닙니다! ;-) developer.mozilla.org/en-US/docs/Web/API/Element/…
Pedro Ferreira

1
이 현대 표준은 멋지고 읽기 쉽지만 speed옵션이 필요합니다 . 현재 스크롤 속도가 너무 느립니다.
Nils Lindemann

31

편집 :이 답변은 2013 년에 작성되었습니다. requestAnimationFrame에 대한 아래 Cristian Traì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
URL에 앵커를 작성하려면 var _updateURL = function ( anchor, url ) { if ( (url === true || url === 'true') && history.pushState ) { history.pushState( {pos:anchor.id}, '', anchor ); } };( Chris Fernandi의 Smooth Scroll js를 통해) 와 같은 방식으로 앵커 URL을 히스토리 스택에 푸시하면 됩니다. 사람들이 "JQuery를 사용"하는 것보다 이것을하는 것을 보니 반갑습니다! 기록을 위해 :target선택 자도 깔끔한 솔루션입니다.
Louis Maddox 2014

1
이 답변은 2013 년에 이미 작성된 requestAnimationFrame은 성능 향상입니다. 왜 여기가 사용되지 않는지 모르겠습니다.
Cristian Traìna

24

연산

요소를 스크롤하려면 scrollTop시간이 지남에 따라 값을 변경해야합니다 . 주어진 시점에 대해 새 scrollTop값을 계산하십시오 . 부드럽게 애니메이션하려면 부드러운 단계 알고리즘을 사용하여 보간 합니다 .

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

내가 알아 낸건 신경 쓰지 마. 사용했다 pageYOffsetscrollTo()element대신 scrollTop.
Mudlabs

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
여기에 관련 코드 스 니핏 또는 작동 방식에 대한 설명을 게시하십시오. 당신의 대답은 내가 아무 것도 클릭 할 필요없이 이해할 수 있어야합니다. 감사합니다!
Edward

감사합니다-그러나 예제를 공유하기 위해 codeopen이 만들어지지 않았습니다-여기에 모든 css / js / html 코드를 넣을 올바른 장소가 아닙니다-util stackoverflow가 필요한 비트를 추가합니다-대부분의 사람들은 당신을 codepen / jsfiddle / 또는 다른 곳으로 보낼 것입니다 ..
SorinN 2014-06-10

1
우리가 그것을 요청하는 이유에 대한 몇 가지 대화는 다음과 같습니다. meta.stackexchange.com/q/149890
Edward

2
나는의 필요성을 이해하지 않는window.setTimeout = window.setTimeout;
pmrotule

6

최신 브라우저는 CSS "scroll-behavior : smooth"속성을 지원합니다. 따라서이를 위해 Javascript가 전혀 필요하지 않습니다. 이것을 body 요소에 추가하고 일반적인 앵커와 링크를 사용하십시오. 스크롤 동작 MDN 문서


Edge와 Safari는 아직이 속성을 지원하지 않습니다. 나는 그들이 곧 그렇게되기를 바랍니다.
6754534367

Firefox에서도 잘 작동하지 않습니다. 그래서 그것은 해결책이 아닙니다.
huseyin39

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()함수는 사용 밝히려 모듈 패턴 의 상기 대상 소자의 ID를 전달 scrolling()통해 함수 scroll.To('id')에 의해 사용되는 값으로 설정하는 scroller()기능.

고장

에서 scrolling():

  • el : 대상 DOM 객체
  • elPos: elememtPosition()호출 될 때마다 페이지 상단을 기준으로 대상 요소의 위치를 ​​제공 하는 함수를 반환합니다 .
  • duration : 전환 시간 (밀리 초).
  • increment : 대상 요소의 시작 위치를 40 단계로 나눕니다.
  • time : 각 단계의 타이밍을 설정합니다.
  • prev:에서 대상 요소의 이전 위치 scroller().
  • E:에서 대상 요소의 위치를 ​​유지합니다 scroller().

실제 작업은 대상 요소가 페이지의 맨 위에 있거나 페이지가 더 이상 스크롤 할 수 없을 때까지 scroller()계속 자신을 호출 하는 함수 (를 통해 setTimeout())에 의해 수행됩니다 .

scroller()호출 될 때마다 대상 요소의 현재 위치 (variable에 유지됨 E)를 확인하고 그것이 > 1OR < -1이고 페이지가 여전히 스크롤 가능한 경우 창을 increment픽셀 단위 로 이동합니다 ( E양수 또는 음수 값에 따라 위 또는 아래로) . 경우 E인도 > 1OR < -1또는 E=== prev기능이 정지한다. DOMElement.scrollTo()대상 요소가 창의 맨 위에 있는지 확인하기 위해 완료시 메서드를 추가했습니다 (픽셀의 일부만 표시되는 것은 아닙니다!).

if2 행 의 명령문 은 이전 위치 ( )를 scroller()확인하여 페이지가 스크롤 중인지 확인합니다 (대상이 페이지 하단을 향하고 페이지가 더 이상 스크롤 할 수없는 경우 ). Eprev

그 아래의 삼항 조건 은 0에 가까워 지면 increment값을 줄 E입니다. 이렇게하면 페이지가 한 방향으로 오버 슈팅 한 다음 다시 바운스되어 다른 쪽을 오버 슈트 한 다음 다시 바운스되어 다른 쪽을 오버 슈트, 핑퐁 스타일, 무한대 및 그 이상으로 중지됩니다.

페이지가 c.4000px 이상인 경우 삼항 표현식의 첫 번째 조건 (여기서는 +/- 20) 및 / 또는 increment값 을 설정하는 제수 (여기서는 40) 의 값을 늘릴 수 있습니다 .

에 대해 재생 duration, 세트 제수 increment의 삼항 조건, 그리고 값이 scroller()맞춤 당신을 허용해야하는 기능은 페이지를 이용할 수 있습니다.

  • JSFiddle

  • NB Lubuntu의 최신 버전의 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

stackoverflow에 오신 것을 환영합니다. 답변 해 주셔서 감사하지만 질문은 jquery를 사용하지 않는 것에 관한 것이 었습니다.
Stephane L
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.