平滑滚动而不使用jQuery

我编写了一个页面,我只想使用原始的JavaScript代码的UI没有任何插件或框架的干扰。

而现在我正在努力寻找一种方法来滚动页面顺利没有jQuery。

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

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

相关链接:

我做到了。 下面的代码不依赖于任何框架。

限制:主动激活不写入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; }; })(); 

algorithm

滚动元素需要随着时间的推移改变其scrollTop值。 对于给定的时间点,计算一个新的scrollTop值。 要平滑地进行animation处理,请使用平滑步骤algorithm进行插值。

计算scrollTop如下:

 var point = smooth_step(start_time, end_time, now); var scrollTop = Math.round(start_top + (distance * point)); 

哪里:

  • start_time是animation开始的时间;
  • end_time是animation结束的(start_time + duration) ;
  • start_top是开始处的scrollTop值; 和
  • distance是期望的最终值和起始值(target - start_top)之间的(target - start_top)

一个强大的解决scheme应该检测animation何时中断,等等。 有关详细信息,请阅读有关无需jQuery的Smooth Scrolling的文章。

演示

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

我在这里做了一个没有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; } } } 

我最近开始解决这个问题,在jQuery不是一个选项的情况下,所以我在这里login我的解决scheme,为了后代。

 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()函数使用Revealing模块模式通过scroll.To('id')将目标元素的id传递给它的scrolling()函数,该函数设置了scroller()函数使用的值。

分解

scrolling()

  • el :目标DOM对象
  • elPos :通过elememtPosition()函数返回一个函数,该函数在elememtPosition()都给出目标元素相对于页面顶部的位置。
  • duration :以毫秒为单位的转换时间。
  • increment :将目标元素的起始位置分成40个步骤。
  • time :设定每一步的时间。
  • prev :目标元素在scroller()的前一个位置。
  • E :在scroller()保存目标元素的位置。

实际的工作由scroller()函数完成,该函数继续通过setTimeout()直接调用自己,直到目标元素位于页面的顶部,或者页面不能再滚动。

每次调用scroller() ,都会检查目标元素的当前位置(保存在variablesE ),如果该值> 1 OR < -1并且页面仍然可以滚动,则按increment像素移动窗口 – 向上或向下如果E是正值或负值。 当E不是> 1< -1E ===时,函数停止。 我在完成时添加了DOMElement.scrollTo()方法,只是为了确保目标元素在窗口的顶部是砰的一声(而不是你会注意到它是由一个像素的一小部分!)。

在第二行的scroller()if语句检查是否滚动页面(在目标可能是朝向页面的底部,页面可以滚动不再进一步)通过检查E对其以前的位置( prev ) 。

E接近零时,下面的三元条件减小increment值。 这样就阻止了页面超出一个方向,然后反弹到另一个超过另一个方向,然后又跳回到另一个,乒乓球风格,超越无限。

如果您的页面高于c.4000px,您可能需要增加三元expression式的第一个条件(此处为+/- 20)和/或设置increment值(此处为40)的除数。

duration播放,设置increment的除数,以及scroller()的三值条件中的值应该允许您调整函数以适合您的页面。

  • 的jsfiddle

  • NB在Lubuntu上使用最新版本的Firefox和Chrome,Windows8上使用Firefox,Chrome和IE。

本地浏览器在JavaScript中的平滑滚动是这样的:

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

我做了这样的事情。 我不知道它是否在IE8中工作。 testingIE9,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元素和chchck,如果它有属性“href”如果是的话,摆脱“#”
  • 不带“#”的elem pointersvariables
  • elemOffset从页面顶部的“滚动到”元素的偏移量
 <script> var set=0; function animatescroll(x,y){ if(set==0){ var val71=x/1000; 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的id

注意:为了让身体滚动给身体一个ID。