【问题标题】:Scroll smoothly to specific element on page平滑滚动到页面上的特定元素
【发布时间】:2013-07-17 08:19:03
【问题描述】:

我想在页面的开头有 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/

【问题讨论】:

标签: javascript jquery animation scroll jquery-animate


【解决方案1】:

requestAnimationFrame 超级流畅

对于平滑渲染的滚动动画,可以使用window.requestAnimationFrame(),而不是常规的setTimeout() 解决方案performs better with rendering

一个基本示例如下所示。浏览器的每个动画帧都会调用函数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/

【讨论】:

  • 只是根据你的来源做一个小图书馆,在学分中链接。谢谢!
  • @Dev_NIX 很高兴我能帮上忙!曾经我使用过sweet-scroll 库,但不知何故它不能很好地与 React 配合使用。您可以重用其中的一些 API 或代码。
  • 这太恶心了。谢谢!
  • 谢谢,你最好! :)
【解决方案2】:

问题是 5 年前提出的,当时我正在与 smooth scroll 打交道,觉得为那些正在寻找的人提供一个简单的解决方案是值得的。所有的答案都很好,但这里是一个简单的答案。

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

只需在源element 上的onClick 事件上调用smoothScroll 函数。

文档:https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

注意:请检查兼容性here

第三方编辑

2020 年对Element.scrollIntoView() 的支持是这样的:

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%

【讨论】:

  • scrollIntoView with 'smooth' 不支持 IE、EDGE 和 Safari (2018/09/28)
  • @KarediaNoorsil 感谢您的报告,更新了我的答案。
  • 2020 年最佳答案。
  • 例如,如果网站有粘性标题,如何抵消滚动?
  • 我认为这是最好的答案。我发现许多其他解决方案在某些移动设备上效果不佳。卡住了一段时间然后开始滚动。这是唯一没有发生的。
【解决方案3】:

刚刚在下面制作了这个仅限 javascript 的解决方案。

简单用法:

EPPZScrollTo.scrollVerticalToElementById('signup_form', 20);

引擎对象(你可以摆弄过滤器、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);
    }
};

【讨论】:

    【解决方案4】:

    平滑滚动 - 看起来不像 jQuery

    ​​>

    基于an article on itnewb.com,我制作了一个没有外部库的demo plunk to smoothly scroll

    javascript 非常简单。首先是一个辅助函数来改进跨浏览器支持以确定当前位置。

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

    要调用它,您只需执行以下操作。您可以使用 id 作为destination anchor 的引用来创建指向另一个元素的链接。

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

    【讨论】:

    • 谢谢,这为我节省了一些时间 :) 我还添加了 smoothScroll({eID, padding = 0}),然后在 let stopY = elmYPosition(eID); 之后添加了 stopY += padding;,以便在元素周围进行一些填充,而不是滚动到确切的元素
    • 将字符串作为函数执行...... :( 为什么不只是:setTimeout(window.scrollTo.bind(null, 0, leapY), timer * speed);
    【解决方案5】:

    您还可以查看这个很棒的博客 - 使用一些非常简单的方法来实现这一点:)

    https://css-tricks.com/snippets/jquery/smooth-scrolling/

    喜欢(来自博客)

    // Scroll to specific values
    // scrollTo is the same
    window.scroll({
      top: 2500, 
      left: 0, 
      behavior: 'smooth'
    });
    
    // Scroll certain amounts from current position 
    window.scrollBy({ 
      top: 100, // could be negative value
      left: 0, 
      behavior: 'smooth' 
    });
    
    // Scroll to a certain element
    document.querySelector('.hello').scrollIntoView({ 
      behavior: 'smooth' 
    });
    

    您还可以像下面这样(或其他方式)获得元素“顶部”位置

    var e = document.getElementById(element);
    var top = 0;
    
    do {   
        top += e.offsetTop;
    } while (e = e.offsetParent);
    
    return top;
    

    【讨论】:

      【解决方案6】:

      为什么不使用 CSS scroll-behavior 属性

      html {
        scroll-behavior: smooth;
      }
      

      浏览器支持也不错 https://caniuse.com/#feat=css-scroll-behavior

      【讨论】:

      • Safari 就像 IE。有些人由于惯性或缺乏知识仍在使用它。
      【解决方案7】:

      我已经用了很长时间了:

      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) 其中elementdocument.getElementById('elementid')

      【讨论】:

      • 这对我来说非常有效。我只是觉得用分号会更好!
      • 只是想添加 - 我的目标是不使用任何外部库,没有必要为页面滚动加载巨大的 jquery ;-)
      • 您好,感谢您的解决方案。 :) 你能告诉我变量window._TO 有什么用吗?我无法弄清楚它的作用以及我们为什么使用 clearTimeout 。我已经删除了clearTimeout,它对我来说非常好。
      • @Subham Tripathi 如果您在完成之前再次调用该函数,它将表现得非常糟糕 - 因为它会继续移动到第一个点,如果第二个调用想要将它移动到不同的点,它将是只是在那里和回来 - 永远
      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        更全面的平滑滚动方法列表见我的回答here


        要在准确的时间内滚动到某个位置,可以使用window.requestAnimationFrame,每次计算适当的当前位置。要滚动到某个元素,只需将 y 位置设置为 element.offsetTop

        /*
           @param pos: the y-position to scroll to (in pixels)
           @param time: the exact amount of time the scrolling will take (in milliseconds)
        */
        function scrollToSmoothly(pos, time) {
            var currentPos = window.pageYOffset;
            var start = null;
            if(time == null) time = 500;
            pos = +pos, time = +time;
            window.requestAnimationFrame(function step(currentTime) {
                start = !start ? currentTime : start;
                var progress = currentTime - start;
                if (currentPos < pos) {
                    window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
                } else {
                    window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
                }
                if (progress < time) {
                    window.requestAnimationFrame(step);
                } else {
                    window.scrollTo(0, pos);
                }
            });
        }
        

        演示:

        function scrollToSmoothly(pos, time) {
            var currentPos = window.pageYOffset;
            var start = null;
            if(time == null) time = 500;
            pos = +pos, time = +time;
            window.requestAnimationFrame(function step(currentTime) {
                start = !start ? currentTime : start;
                var progress = currentTime - start;
                if (currentPos < pos) {
                    window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
                } else {
                    window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
                }
                if (progress < time) {
                    window.requestAnimationFrame(step);
                } else {
                    window.scrollTo(0, pos);
                }
            });
        }
        
        document.getElementById("toElement").addEventListener("click", function(e){
          scrollToSmoothly(document.querySelector('div').offsetTop, 500 /* milliseconds */);
        });
        document.getElementById("backToTop").addEventListener("click", function(e){
          scrollToSmoothly(0, 500);
        });
        <button id="toElement">Scroll To Element</button>
        <div style="margin: 1000px 0px; text-align: center;">Div element
          <button id="backToTop">Scroll back to top</button>
        </div>

        也可以使用SmoothScroll.js library,它支持滚动到页面上的一个元素,除了更复杂的功能,例如垂直和水平平滑滚动,在其他容器元素内滚动,不同的缓动行为,相对滚动当前位置等。

        document.getElementById("toElement").addEventListener("click", function(e){
          smoothScroll({toElement: document.querySelector('div'), duration: 500});
        });
        document.getElementById("backToTop").addEventListener("click", function(e){
          smoothScroll({yPos: 'start', duration: 500});
        });
        <script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll@1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
        <button id="toElement">Scroll To Element</button>
        <div style="margin: 1000px 0px; text-align: center;">Div element
          <button id="backToTop">Scroll back to top</button>
        </div>

        或者,您可以将选项对象传递给 window.scroll 滚动到特定的 x 和 y 位置,window.scrollBy 从当前位置滚动一定量:

        // Scroll to specific values
        // scrollTo is the same
        window.scroll({
          top: 2500, 
          left: 0, 
          behavior: 'smooth' 
        });
        
        // Scroll certain amounts from current position 
        window.scrollBy({ 
          top: 100, // could be negative value
          left: 0, 
          behavior: 'smooth' 
        });
        

        如果您只需要滚动到一个元素,而不是文档中的特定位置,您可以使用Element.scrollIntoView 并将behavior 设置为smooth

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

        【讨论】:

          【解决方案10】:

          你可以使用这个插件。做你想做的事。

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

          【讨论】:

          • 所以我需要这样编码: $('#id1').localScroll({ target:'#content1' });
          • 并且必须导入所有 jQuery 库才能滚动。像其他答案一样,使用 vanila js 做得更好。
          【解决方案11】:

          如果需要滚动到 div 内的元素,我的解决方案是基于 Andrzej Sala's answer:

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

          【讨论】:

            【解决方案12】:

            使用 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 plugin

            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 上完全 working demo

            你可以看看soucre code for the demo.

            2014 年 5 月更新

            基于另一个问题,我遇到了来自 kadajanother solution。这里jQuery animate用于滚动到&lt;div style=overflow-y: scroll&gt;内的元素

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

            【讨论】:

            • 与此同时,我制作了这个小提琴:jsfiddle.net/WxJLx/15.. 它在小提琴中工作,但在 wordpress 上没有。如果你能发现任何问题,你能检查这个源代码吗?谢谢:查看源:anchovyluxury.com/yachts/services
            • 你试图找出错误是什么?您是否熟悉 chrome 开发工具 - 它们让您非常容易发现错误:$('a[href^="#"]').click(function(){ in line 360​​ of anchovyluxury.com/yachts/services 您还有关于如何平滑滚动的问题吗?
            • 我试图输入弹出 jquery 正在工作的窗口的代码。而且我的代码在小提琴中工作,所以不知道问题出在哪里。
            猜你喜欢
            • 2013-06-15
            • 2011-07-02
            • 2020-07-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-13
            相关资源
            最近更新 更多