不使用 jQuery 的平滑滚动

IT技术 javascript html
2021-01-24 21:43:25

我正在编写一个页面,我只想将原始 JavaScript 代码用于 UI,而不受插件或框架的任何干扰。

现在我正在努力寻找一种无需 jQuery 即可平滑滚动页面的方法。

6个回答

原生浏览器在 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' 
});
@Vishu Rana,行为支持仅适用于MDN 上所述的 firefox
2021-03-20 21:43:25
@EvandroCavalcateSantos:不再!;-) developer.mozilla.org/en-US/docs/Web/API/Element/...
2021-03-22 21:43:25
我创建了一个带有速度的演示:codepen.io/Niekes/pen/poEqaBW你可以通过改变“duration”变量来控制速度。
2021-03-24 21:43:25
这个现代标准很好而且可读,但需要一个speed选项。当前的滚动速度对于流量来说太慢了。
2021-03-28 21:43:25
你能放上演示链接吗
2021-04-08 21:43:25

试试这个平滑滚动演示,或者像这样的算法:

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

相关链接:

好的大纲,但请不要创建一个字符串来执行超时,继续创建一个调用实际函数的匿名函数。
2021-03-31 21:43:25
@nness 真可惜。我真的很想看看代码。
2021-04-03 21:43:25

算法

滚动元素需要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);
    });
}
没关系我想通了。不得不使用pageYOffsetand scrollTo()onelement而不是scrollTop.
2021-03-19 21:43:25
这似乎是一个非常好的解决方案。但是你怎么能在 window 对象上实现它而不是像你在 Fiddle 中那样的元素呢?
2021-03-25 21:43:25

我在这里做了一个没有 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;
        }
    }
 }
我不明白需要 window.setTimeout = window.setTimeout;
2021-03-21 21:43:25
谢谢 - 但不是 - codeopen 用于分享示例 - 这里不是放置所有 css / js / html 代码的正确位置 - util stackoverflow 将添加必要的位 - 大多数人会将您发送到 codepen / jsfiddle / 或其他地方..
2021-04-02 21:43:25
以下是关于我们为什么要求这样做的一些对话:meta.stackexchange.com/q/149890
2021-04-05 21:43:25
请在此处发布相关代码片段或对其工作原理的描述。您的回答应该是有意义的,而我无需单击任何内容。谢谢!
2021-04-12 21:43:25

现代浏览器支持 CSS “scroll-behavior: smooth” 属性。因此,我们甚至根本不需要任何 Javascript。只需将其添加到 body 元素,并使用常用的锚点和链接。 滚动行为 MDN 文档

它不能很好地工作……即使在 Firefox 中也是如此。所以这不是一个解决方案。
2021-03-17 21:43:25
只有一个速度。糟透了。
2021-03-23 21:43:25
Edge 和 Safari 都不支持这个属性。我希望他们很快。
2021-04-10 21:43:25