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


83

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

І зараз я намагаюся знайти спосіб плавного прокручування сторінки без jQuery.


3
Так само, як це зробив би jQuery. Розбийте анімацію на ряд дуже маленьких кроків, використовуйте таймер інтервалу з невеликим інтервалом, щоб виконувати кожен з цих кроків по черзі, поки вони не будуть зроблені.
tvanfosson

Я думав про це рішення на початку, і єдиний момент, який я пропустив, - це насправді в посиланні Камала нижче, тобто як розрахувати Y-позиції об’єктів. Дякую tvanfosson :)

Погляньте, будь ласка, на мою відповідь про плавну прокрутку за допомогою простого js
surfmuggle

Це схоже на одне з моїх запитань. Перевірте stackoverflow.com/questions/40598955/… .
Канніцид

Відповіді:


27

Спробуйте цю демонстраційну плавну прокрутку або такий алгоритм, як:

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

Пов’язані посилання:


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 Rana, підтримка поведінки надходить лише до firefox, як описано на MDN
Evandro Cavalcate Santos


2
Цей сучасний стандарт є приємним і читабельним, але потребує speedопції. Поточна швидкість прокрутки занадто повільна для потоку.
Нільс Ліндеманн,

31

редагувати: ця відповідь була написана в 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;
};
})();

2
Думаю, ви просто натиснете URL-адресу прив'язки до стеку історії чимось на зразок var _updateURL = function ( anchor, url ) { if ( (url === true || url === 'true') && history.pushState ) { history.pushState( {pos:anchor.id}, '', anchor ); } };( за допомогою плавного прокручування js Кріса Фернанді ), щоб записати прив'язку до URL-адреси. Приємно бачити, як люди роблять це, а не просто “використовують JQuery”! Для запису :targetселектори також є чудовим рішенням.
Луї Меддокс,

1
Ця відповідь з 2013 року, коли вже було написано requestAnimationFrame, що є підвищенням продуктивності. Я не знаю, чому тут не використовується.
Крістіан Трейна,

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_top- scrollTopзначення на початку; і
  • 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);
    });
}

Це здається справді хорошим рішенням. Але як ви можете реалізувати його на віконному об'єкті замість елемента, як у вашому скрипті?
Mudlabs

Неважливо, я це зрозумів. Довелося використовувати pageYOffsetі scrollTo()на 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
Будь ласка, розмістіть тут відповідні фрагменти коду або опис того, як це працює. Ваша відповідь повинна мати сенс, без того, щоб я нічого не натискав. Дякую!
Едвард

дякую - але ні - codeopen створений для обміну прикладами - тут не найкраще розмістити весь код css / js / html - util stackoverflow додасть необхідні біти - більшість людей надішле вас до codepen / jsfiddle / або деінде.
SorinN

1
Ось кілька бесід про те, чому ми просимо про це: meta.stackexchange.com/q/149890
Едвард

2
Я не розумію необхідностіwindow.setTimeout = window.setTimeout;
pmrotule

6

Сучасні браузери підтримують властивість CSS "поведінка прокрутки: гладка". Отже, нам для цього навіть не потрібен будь-який 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()Функція використовує показова модуль Pattern , щоб передати ідентифікатор цільового елемента до його 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наближаючись до нуля. Це зупиняє перебіг сторінки в один бік, а потім відскакуючи, щоб перебігти інший, а потім відскакуючи, щоб перебігти інший знову, у стилі пінг-понгу, до нескінченності та не тільки.

Якщо ваша сторінка перевищує c.4000px, можливо, ви захочете збільшити значення у першій умові тернарного виразу (тут на +/- 20) та / або дільнику, який встановлює incrementзначення (тут на 40).

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

  • JSFiddle

  • NBТестується в сучасних версіях Firefox та Chrome на Lubuntu та Firefox, Chrome та IE на Windows8.


3

Ви також можете скористатися властивістю поведінки прокрутки . наприклад, додайте нижній рядок до вашого css

html{
scroll-behavior:smooth;
}

і це призведе до рідної функції плавної прокрутки. Докладніше про поведінку прокрутки


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—Get елемент і chceck, якщо він має атрибут "href", якщо так, позбудьтеся "#"
  • elem—Змінна покажчика без "#"
  • elemOffset—Зсув елемента "прокручування до" вгорі сторінки

2

Можна використовувати

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

Якщо ви хочете прокрутити верх сторінки, ви можете просто розмістити порожній елемент у верхній частині та плавно прокрутити до цього.


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

Демо:


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 = id div, який використовується для прокрутки

Примітка: Щоб зробити тіло для прокрутки, дайте тілу 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.
Стефан Л
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.