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

Я хочу иметь 4 кнопки/ссылки в начале страницы, а под ними контент.

На кнопки ставлю такой код:

<a href="#idElement1">Scroll to element 1</a>
<a href="#idElement2">Scroll to element 2</a>
<a href="#idElement3">Scroll to element 3</a>
<a href="#idElement4">Scroll to element 4</a>

А под ссылками будет контент:

<h2 id="idElement1">Element1</h2>
content....
<h2 id="idElement2">Element2</h2>
content....
<h2 id="idElement3">Element3</h2>
content....
<h2 id="idElement4">Element4</h2>
content....

Теперь он работает, но не может сделать его более гладким.

Я использовал этот код, но не могу заставить его работать.

$('html, body').animate({
    scrollTop: $("#elementID").offset().top
}, 2000);

Какие-либо предложения? Спасибо.

Изменить: и скрипка: http://jsfiddle.net/WxJLx/2/


person M P    schedule 18.07.2013    source источник
comment
возможный дубликат Плавная прокрутка JavaScript/jQuery до элемента   -  person MDEV    schedule 18.07.2013
comment
я должен спросить, вы использовали код анимации внутри события клика?   -  person Or Duan    schedule 18.07.2013
comment
Боюсь, я не знаю, о чем ты меня спрашиваешь   -  person M P    schedule 18.07.2013
comment
$('#idElement1').onclick=function(){/*вот ваш код плавной прокрутки*/}   -  person dbanet    schedule 18.07.2013
comment
также не забудьте вернуть false, чтобы отключить встроенную прокрутку   -  person dbanet    schedule 18.07.2013
comment
можете ли вы показать мне эту скрипку, пожалуйста: jsfiddle.net/WxJLx/2   -  person M P    schedule 18.07.2013
comment
Мне непонятно, хотите ли вы найти решение для плавной прокрутки или хотите узнать и понять, почему ваш код не работает.   -  person surfmuggle    schedule 20.07.2013


Ответы (12)


Только что сделал это решение только для javascript ниже.

Простое использование:

EPPZScrollTo.scrollVerticalToElementById('signup_form', 20);

Объект Engine (вы можете возиться с фильтром, значениями fps):

/**
 *
 * Created by Borbás Geri on 12/17/13
 * Copyright (c) 2013 eppz! development, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */


var EPPZScrollTo =
{
    /**
     * Helpers.
     */
    documentVerticalScrollPosition: function()
    {
        if (self.pageYOffset) return self.pageYOffset; // Firefox, Chrome, Opera, Safari.
        if (document.documentElement && document.documentElement.scrollTop) return document.documentElement.scrollTop; // Internet Explorer 6 (standards mode).
        if (document.body.scrollTop) return document.body.scrollTop; // Internet Explorer 6, 7 and 8.
        return 0; // None of the above.
    },

    viewportHeight: function()
    { return (document.compatMode === "CSS1Compat") ? document.documentElement.clientHeight : document.body.clientHeight; },

    documentHeight: function()
    { return (document.height !== undefined) ? document.height : document.body.offsetHeight; },

    documentMaximumScrollPosition: function()
    { return this.documentHeight() - this.viewportHeight(); },

    elementVerticalClientPositionById: function(id)
    {
        var element = document.getElementById(id);
        var rectangle = element.getBoundingClientRect();
        return rectangle.top;
    },

    /**
     * Animation tick.
     */
    scrollVerticalTickToPosition: function(currentPosition, targetPosition)
    {
        var filter = 0.2;
        var fps = 60;
        var difference = parseFloat(targetPosition) - parseFloat(currentPosition);

        // Snap, then stop if arrived.
        var arrived = (Math.abs(difference) <= 0.5);
        if (arrived)
        {
            // Apply target.
            scrollTo(0.0, targetPosition);
            return;
        }

        // Filtered position.
        currentPosition = (parseFloat(currentPosition) * (1.0 - filter)) + (parseFloat(targetPosition) * filter);

        // Apply target.
        scrollTo(0.0, Math.round(currentPosition));

        // Schedule next tick.
        setTimeout("EPPZScrollTo.scrollVerticalTickToPosition("+currentPosition+", "+targetPosition+")", (1000 / fps));
    },

    /**
     * For public use.
     *
     * @param id The id of the element to scroll to.
     * @param padding Top padding to apply above element.
     */
    scrollVerticalToElementById: function(id, padding)
    {
        var element = document.getElementById(id);
        if (element == null)
        {
            console.warn('Cannot find element with id \''+id+'\'.');
            return;
        }

        var targetPosition = this.documentVerticalScrollPosition() + this.elementVerticalClientPositionById(id) - padding;
        var currentPosition = this.documentVerticalScrollPosition();

        // Clamp.
        var maximumScrollPosition = this.documentMaximumScrollPosition();
        if (targetPosition > maximumScrollPosition) targetPosition = maximumScrollPosition;

        // Start animation.
        this.scrollVerticalTickToPosition(currentPosition, targetPosition);
    }
};
person Geri Borbás    schedule 18.12.2013

Очень гладко с requestAnimationFrame

Для плавной анимации прокрутки можно использовать window.requestAnimationFrame(), который лучше справляется с рендерингом, чем обычные решения setTimeout().

Базовый пример выглядит так. Функция step вызывается для каждого кадра анимации браузера и позволяет лучше управлять временем перерисовки и, таким образом, повышать производительность.

function doScrolling(elementY, duration) { 
  var startingY = window.pageYOffset;
  var diff = elementY - startingY;
  var start;

  // Bootstrap our animation - it will get called right before next frame shall be rendered.
  window.requestAnimationFrame(function step(timestamp) {
    if (!start) start = timestamp;
    // Elapsed milliseconds since start of scrolling.
    var time = timestamp - start;
    // Get percent of completion in range [0, 1].
    var percent = Math.min(time / duration, 1);

    window.scrollTo(0, startingY + diff * percent);

    // Proceed with animation as long as we wanted it to.
    if (time < duration) {
      window.requestAnimationFrame(step);
    }
  })
}

Для позиции Y элемента используйте функции в других ответах или в моей нижеупомянутой скрипке.

Я установил немного более сложную функцию с поддержкой плавности и правильной прокруткой до самых нижних элементов: https://jsfiddle.net/s61x7c4e/

person Daniel Sawka    schedule 14.09.2016
comment
Просто делаю небольшую библиотеку на основе вашего источника, ссылаясь здесь в титрах. Спасибо! - person Dev_NIX; 14.12.2016
comment
@Dev_NIX Рад, что смог помочь! Однажды я использовал библиотеку sweet-scroll, но она почему-то плохо работала с React. Вы можете повторно использовать некоторый API или код из этого. - person Daniel Sawka; 15.12.2016
comment
Это очень больно. Спасибо! - person BrandonReid; 25.01.2017
comment
Спасибо, ты лучший! :) - person Naskalin; 03.05.2021

Вопрос был задан 5 лет назад, и я имел дело с smooth scroll и чувствовал, что простое решение того стоит тем, кто его ищет. Все ответы хороши, но вот простой.

function smoothScroll(){
    document.querySelector('.your_class or #id here').scrollIntoView({
        behavior: 'smooth'
    });
}

просто вызовите функцию smoothScroll для события onClick в вашем источнике element.

ДОКУМЕНТЫ: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

Примечание. Проверьте совместимость здесь.

Стороннее редактирование

Поддержка Element.scrollIntoView() в 2020 году такова:

Region            full   + partial = sum full+partial Support
Asia              73.24% + 22.75%  = 95.98%
North America     56.15% + 42.09%  = 98.25%
India             71.01% + 20.13%  = 91.14%
Europe            68.58% + 27.76%  = 96.35%

прокрутите страницу поддержки 2020-02-28

person Sanjay Shr    schedule 13.08.2018
comment
scrollIntoView с Smooth не поддерживает IE, EDGE и Safari (28.09.2018) - person Karedia Noorsil; 28.09.2018
comment
@KarediaNoorsil Спасибо за сообщение, обновил мой ответ. - person Sanjay Shr; 29.09.2018
comment
Лучший ответ в 2020 году. - person n8jadams; 20.02.2020
comment
Чувак, как мне тебя отблагодарить? - person Beki; 10.06.2021

Плавная прокрутка — смотри, нет jQuery

На основе статьи на itnewb.com я сделал демонстрация плавной прокрутки без внешних библиотек.

Яваскрипт довольно простой. Сначала вспомогательная функция для улучшения кросс-браузерной поддержки для определения текущей позиции.

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

Чтобы вызвать его, вы просто делаете следующее. Вы создаете ссылку, которая указывает на другой элемент, используя идентификатор в качестве ссылки для якорь назначения.

<a href="#anchor-2" 
   onclick="smoothScroll('anchor-2');">smooth scroll to the headline with id anchor-2<a/>
...
...  some content
...
<h2 id="anchor-2">Anchor 2</h2>

Авторские права

В футере itnewb.com написано следующее: The techniques, effects and code demonstrated in ITNewb articles may be used for any purpose without attribution (although we recommend it) (2014-01-12)

person surfmuggle    schedule 18.07.2013
comment
Спасибо, просто сэкономил мне время :) Я также добавил к smoothScroll({eID, padding = 0}), а затем stopY += padding; после let stopY = elmYPosition(eID);, чтобы иметь некоторые отступы вокруг элемента, а не прокручивать его до конкретного элемента. - person Lukas Liesis; 14.04.2016
comment
Выполнение строки как функции.... :( Почему бы просто не: setTimeout(window.scrollTo.bind(null, 0, leapY), timer * speed); - person monners; 01.09.2016


Я использую это в течение длительного времени:

function scrollToItem(item) {
    var diff=(item.offsetTop-window.scrollY)/8
    if (Math.abs(diff)>1) {
        window.scrollTo(0, (window.scrollY+diff))
        clearTimeout(window._TO)
        window._TO=setTimeout(scrollToItem, 30, item)
    } else {
        window.scrollTo(0, item.offsetTop)
    }
}

использование: scrollToItem(element) где element например document.getElementById('elementid').

person Community    schedule 05.07.2016
comment
Это отлично сработало для меня. Я просто думаю, что было бы лучше с точкой с запятой! - person Vahid Amiri; 23.09.2016
comment
просто хотел добавить - моей целью было не использовать какие-либо внешние библиотеки, нет смысла загружать огромный jquery только для прокрутки страницы ;-) - person ; 04.01.2017
comment
Эй, спасибо за решение. :) Не могли бы вы рассказать мне, для чего нужна переменная window._TO. Я не могу понять, что он делает и почему мы используем clearTimeout . Я удалил clearTimeout, и он отлично работает для меня. - person Subham Tripathi; 10.02.2017
comment
@Subham Tripathi, если вы снова вызовете функцию до ее завершения, она будет вести себя довольно плохо, потому что она будет продолжать двигаться к первой точке, и если второй вызов захочет переместить ее в другую точку, она будет просто туда и обратно - навсегда - person ; 10.02.2017

Вариант ответа @tominko. Немного более плавная анимация и решена проблема с бесконечным вызовом setTimeout(), когда некоторые элементы не могут выровняться по верхней части области просмотра.

function scrollToItem(item) {
    var diff=(item.offsetTop-window.scrollY)/20;
    if(!window._lastDiff){
        window._lastDiff = 0;
    }

    console.log('test')

    if (Math.abs(diff)>2) {
        window.scrollTo(0, (window.scrollY+diff))
        clearTimeout(window._TO)

        if(diff !== window._lastDiff){
            window._lastDiff = diff;
            window._TO=setTimeout(scrollToItem, 15, item);
        }
    } else {
        console.timeEnd('test');
        window.scrollTo(0, item.offsetTop)
    }
}
person Andrzej Sala    schedule 06.11.2016

Почему бы не использовать свойство CSS scroll-behavior?

html {
  scroll-behavior: smooth;
}

Поддержка браузера также хороша https://caniuse.com/#feat=css-scroll-behavior

person esnezz    schedule 09.05.2020
comment
Не работает в сафари :-( - person randomcontrol; 11.04.2021

вы можете использовать этот плагин. Делает именно то, что вы хотите.

http://flesler.blogspot.com/2007/10/jqueryscrollto.html

person Feedthe    schedule 18.07.2013
comment
Поэтому мне нужно было бы кодировать так: $('#id1').localScroll({ target:'#content1' }); - person M P; 18.07.2013
comment
и пришлось бы импортировать всю библиотеку jQuery для прокрутки. Лучше сделать с vanila js, как и другие ответы. - person Lukas Liesis; 14.04.2016

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

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

Если вы хотите перейти к элементу по его id, вы можете добавить эту функцию (вместе с указанной выше функцией):

function scrollSmoothlyToElementById(id){
   var elem = document.getElementById(id);
   scrollToSmoothly(elem.offsetTop);
}

Демо:

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

person Unmitigated    schedule 04.08.2018

Если нужно прокрутить до элемента внутри div, есть мое решение, основанное на ответе Анджея Сала:

function scroolTo(element, duration) {
    if (!duration) {
        duration = 700;
    }
    if (!element.offsetParent) {
        element.scrollTo();
    }
    var startingTop = element.offsetParent.scrollTop;
    var elementTop = element.offsetTop;
    var dist = elementTop - startingTop;
    var start;

    window.requestAnimationFrame(function step(timestamp) {
        if (!start)
            start = timestamp;
        var time = timestamp - start;
        var percent = Math.min(time / duration, 1);
        element.offsetParent.scrollTo(0, startingTop + dist * percent);

        // Proceed with animation as long as we wanted it to.
        if (time < duration) {
            window.requestAnimationFrame(step);
        }
    })
}
person Kuba Szostak    schedule 25.06.2018

Плавная прокрутка с помощью jQuery.ScrollTo

Чтобы использовать плагин jQuery ScrollTo, вам нужно сделать следующее

  1. Создавайте ссылки, где href указывает на другой elements.id
  2. создайте элементы, к которым вы хотите прокрутить
  3. ссылка на jQuery и плагин scrollTo
  4. Обязательно добавьте обработчик события клика для каждой ссылки, которая должна выполнять плавную прокрутку.

Создание ссылок

<h1>Smooth Scrolling with the jQuery Plugin .scrollTo</h1>
<div id="nav-list">
  <a href="#idElement1">Scroll to element 1</a>
  <a href="#idElement2">Scroll to element 2</a>
  <a href="#idElement3">Scroll to element 3</a>
  <a href="#idElement4">Scroll to element 4</a>
</div>

При создании целевых элементов здесь отображаются только первые два, остальные заголовки настраиваются таким же образом. Чтобы увидеть еще один пример, я добавил ссылку на навигацию a.toNav

<h2 id="idElement1">Element1</h2>    
....
<h2 id="idElement1">Element1</h2>
... 
<a class="toNav" href="#nav-list">Scroll to Nav-List</a>

Установка ссылок на скрипты. Ваш путь к файлам может отличаться.

<script src="./jquery-1.8.3.min.js"></script>
<script src="./jquery.scrollTo-1.4.3.1-min.js"></script>

Проводка все это

Приведенный ниже код заимствован из плагина jQuery easing.

jQuery(function ($) {
    $.easing.elasout = function (x, t, b, c, d) {
        var s = 1.70158;  var p = 0; var a = c;
        if (t == 0) return b;
        if ((t /= d) == 1) return b + c;
        if (!p) p = d * .3;
        if (a < Math.abs(c)) {
            a = c;   var s = p / 4;
        } else var s = p / (2 * Math.PI) * Math.asin(c / a);
        // line breaks added to avoid scroll bar
        return a * Math.pow(2, -10 * t)  * Math.sin((t * d - s) 
                 * (2 * Math.PI) / p) + c + b;
    };            

    // important reset all scrollable panes to (0,0)       
    $('div.pane').scrollTo(0); 
    $.scrollTo(0);    // Reset the screen to (0,0)
    // adding a click handler for each link 
    // within the div with the id nav-list
    $('#nav-list a').click(function () {             
        $.scrollTo(this.hash, 1500, {
            easing: 'elasout'
        });
        return false;
    });   
    // adding a click handler for the link at the bottom
    $('a.toNav').click(function () { 
        var scrollTargetId = this.hash;
        $.scrollTo(scrollTargetId, 1500, {
            easing: 'elasout'
        });
        return false;
    });    
});

Полностью рабочая демонстрация на plnkr.co

Вы можете взглянуть на исходный код для демонстрации.

Обновление май 2014 г.

Основываясь на другом вопросе, я нашел другое решение от kadaj. Здесь анимация jQuery используется для прокрутки к элементу внутри <div style=overflow-y: scroll>

 $(document).ready(function () {
    $('.navSection').on('click', function (e) {
        debugger;
        var elemId = "";    //eg: #nav2
        switch (e.target.id) {
        case "nav1":
            elemId = "#s1";
            break;
        case "nav2":
            elemId = "#s2";
            break;
        case "nav3":
            elemId = "#s3";
            break;
        case "nav4":
            elemId = "#s4";
            break;
        }
        $('.content').animate({
            scrollTop: $(elemId).parent().scrollTop() 
                    + $(elemId).offset().top 
                    - $(elemId).parent().offset().top
        }, {
            duration: 1000,
            specialEasing: { width: 'linear'
                    , height: 'easeOutBounce' },
            complete: function (e) {
                //console.log("animation completed");
            }
        });
        e.preventDefault();
    });
  });
person surfmuggle    schedule 19.07.2013
comment
Тем временем я сделал эту скрипку: jsfiddle.net/WxJLx/15.. она работает на скрипке, но не на wordpress. Можете ли вы проверить этот исходный код, если обнаружите какие-либо проблемы? Спасибо: источник просмотра:anchovyluxury.com/yachts/services - person M P; 19.07.2013
comment
Что вы пытались найти ошибку? Знакомы ли вы с инструментами разработчика Chrome? С их помощью очень легко выявлять ошибки: $('a[href^="#"]').click(function(){ в строке 360 anchovyluxury. com/yachts/services Есть еще вопросы по плавной прокрутке? - person surfmuggle; 19.07.2013
comment
Я попытался вставить этот код, в котором всплывает окно, в котором работает jquery. И мой код работает на скрипке, поэтому не знаю, в чем проблема. - person M P; 20.07.2013