Плавная прокрутка без использования jQuery

Я кодирую страницу, на которой я хочу использовать только сырой код JavaScript для пользовательского интерфейса без какого-либо вмешательства плагинов или платформ.

А сейчас я пытаюсь найти способ плавно прокручивать страницу без использования jQuery.

66 голосов | спросил user743234 8 PMpSun, 08 Apr 2012 18:03:24 +040003Sunday 2012, 18:03:24

10 ответов


0

Попробуйте это демонстрация с плавной прокруткой или такой алгоритм:

  1. Получить текущее верхнее местоположение, используя self.pageYOffset
  2. Получите позицию элемента до того места, где вы хотите перейти к: element.offsetTop
  3. Выполните цикл for, чтобы добраться до него, что будет довольно быстро, или используйте таймер для плавной прокрутки до этой позиции, используя window.scrollTo

См. также другой популярный ответ на этот вопрос.


Оригинальный код Эндрю Джонсона:

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

Ссылки по теме:

ответил Kamal 8 PMpSun, 08 Apr 2012 18:07:36 +040007Sunday 2012, 18:07:36
0

изменить: этот ответ был написан в 2013 году. Проверьте комментарий Кристиана Трайны ниже о requestAnimationFrame

Я сделал это. Код ниже не зависит от какой-либо платформы.

Ограничение: активный якорь не записан в 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;
};
})();
ответил Gabriel 6 32013vEurope/Moscow11bEurope/MoscowWed, 06 Nov 2013 13:24:09 +0400 2013, 13:24:09
0

Алгоритм

Прокрутка элемента требует изменения его значения 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_top - это значение scrollTop в начале ; и
  • distance - это разница между желаемым конечным значением и начальным значением (target - start_top)

Надежное решение должно обнаруживать прерывание анимации и многое другое. Прочитайте мой пост о плавной прокрутке без jQuery для получения подробной информации.

Demo

См. 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);
    });
}
ответил hasen 29 PM000000100000003831 2014, 22:38:38
0

Собственная плавная прокрутка в браузере в 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' 
});
ответил 30 PMpSun, 30 Apr 2017 12:00:19 +030000Sunday 2017, 12:00:19
0

Я сделал пример без 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;
        }
    }
 }
ответил SorinN 21 Mayam14 2014, 01:19:36
0

Недавно я решил решить эту проблему в ситуации, когда 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() использует раскрытие Шаблон модуля для передачи идентификатора целевого элемента в функцию scrolling() через scroll.To('id'), который устанавливает значения, используемые функцией scroller().

Структура

В scrolling():

  • el: целевой объект DOM
  • elPos: возвращает функцию через elememtPosition(), которая дает позицию целевого элемента относительно верхней части страницы каждый раз, когда она вызывается.
  • duration: время перехода в миллисекундах.
  • increment: делит начальную позицию целевого элемента на 40 шагов.
  • time: устанавливает время каждого шага.
  • prev: предыдущая позиция целевого элемента в scroller().
  • E: содержит позицию целевого элемента в scroller().

Фактическая работа выполняется функцией scroller(), которая продолжает вызывать себя (через setTimeout()) до тех пор, пока целевой элемент не окажется вверху страницы или страница не сможет прокрутить больше.

Каждый раз, когда вызывается scroller(), проверяется текущая позиция целевого элемента (содержится в переменной E) и если это > 1 ИЛИ < -1 и, если страница все еще прокручивается, окно сдвигается на increment - вверх или вниз в зависимости от того, является ли E положительным или отрицательным значением. Когда E не является ни > 1 ИЛИ < -1 или E === prev функция останавливается. Я добавил метод DOMElement.scrollTo() по завершении, просто чтобы убедиться, что целевой элемент ударил по верхней части окна (не то, что вы заметили бы) это на долю пикселя!).

Оператор if в строке 2 scroller() проверяет, прокручивается ли страница (в тех случаях, когда цель может быть ближе к нижней части страницы, а страница больше не может прокручиваться), проверяя E относительно своей предыдущей позиции (prev).

Тройное условие под ним уменьшает значение increment как E приближается к нулю. Это останавливает перелистывание страницы в одну сторону, а затем отскакивает назад, чтобы перескочить в другую, а затем снова отскакивает, чтобы снова перескочить в другую, в стиле пинг-понга, до бесконечности и дальше.

Если ваша страница имеет высоту более 4000 пикселей, возможно, вы захотите увеличить значения первого условия троичного выражения (здесь +/- 20) и /или делителя, который устанавливает increment значение (здесь на 40).

Игра с duration, делителем которого устанавливается increment, а значения в троичном условии scroller() должны позволить вам адаптировать функцию к вашей странице.

  • JSFiddle

  • N.B. Испытано в современных версиях Firefox и Chrome для Lubuntu, а также Firefox, Chrome и IE для Windows8.

ответил Brian Peacock 21 +03002015-10-21T21:08:24+03:00312015bEurope/MoscowWed, 21 Oct 2015 21:08:24 +0300 2015, 21:08:24
0

Я сделал что-то вроде этого. Я понятия не имею, работает ли он в 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 - получить элемент и chceck, если он имеет атрибут "href", если да избавиться от "#"
  • elem - переменная указателя без "#"
  • elemOffset - смещение элемента «прокрутка до» в верхней части страницы
ответил Skjal 17 +03002016-10-17T01:10:23+03:00312016bEurope/MoscowMon, 17 Oct 2016 01:10:23 +0300 2016, 01:10:23
0

Вы можете использовать

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

Если вы хотите прокрутить верхнюю часть страницы, вы можете просто разместить пустой элемент в верхней части и плавно перейти к этому элементу.

ответил Antonio Brandao 31 Jpm1000000pmWed, 31 Jan 2018 22:32:53 +030018 2018, 22:32:53
0

Вы можете использовать цикл for с window.scrollTo и setTimeout для плавной прокрутки с помощью простого Javascript. Чтобы перейти к элементу с моей функцией scrollToSmoothly: scrollToSmoothly(elem.offsetTop) (при условии, что elem является элементом DOM). Вы можете использовать это для плавной прокрутки к любой 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);
      }
}

Демо:

 <button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 1000px 0px; text-align: center;">Div element<p/>
<button onClick="scrollToSmoothly(Number(0))">Scroll back to top</button>
</div>
<script>
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);
      }
}
function scrollToDiv(){
  var elem = document.querySelector("div");
  scrollToSmoothly(elem.offsetTop);
}
</script>
ответил hev1 4 PM000000110000000531 2018, 23:37:05
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, который используется для прокрутки

Примечание. Для того, чтобы сделать тело для прокрутки, дайте телу идентификатор.

ответил Jacob Mathen Alex 1 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowFri, 01 Sep 2017 10:54:26 +0300 2017, 10:54:26

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132