平滑滚动而不使用jQuery
我编写了一个页面,我只想使用原始的JavaScript代码的UI没有任何插件或框架的干扰。
而现在我正在努力寻找一种方法来滚动页面顺利没有jQuery。
试试这个平滑的滚动演示 ,或者像这样的algorithm:
- 使用
self.pageYOffset
获取当前的最高位置 - 获取元素的位置,直到您要滚动到的位置:
element.offsetTop
- 做一个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
或< -1
或E
===时,函数停止。 我在完成时添加了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。