我正在编写一个页面,我只想对 UI 使用原始 JavaScript 代码,而不受插件或框架的任何干扰。
现在,我正在努力寻找一种无需 jQuery 即可顺利滚动页面的方法。
我正在编写一个页面,我只想对 UI 使用原始 JavaScript 代码,而不受插件或框架的任何干扰。
现在,我正在努力寻找一种无需 jQuery 即可顺利滚动页面的方法。
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'
});
试试这个平滑滚动演示,或类似的算法:
self.pageYOffset
element.offsetTop
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++;
}
}
相关链接:
滚动一个元素需要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_top
是scrollTop
开始时的值;和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);
});
}
我在这里做了一个没有 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;
}
}
}
现代浏览器支持 CSS“滚动行为:平滑”属性。因此,我们甚至根本不需要任何 Javascript。只需将其添加到 body 元素,并使用通常的锚点和链接。 滚动行为 MDN 文档
您可以使用新的 滚动行为CSS 属性。
例如,将以下行添加到您的 CSS。
html{
scroll-behavior:smooth;
}
这将产生原生的平滑滚动功能。
在此处查看演示
所有现代浏览器都支持滚动行为属性。
我最近开始在无法选择 jQuery 的情况下解决这个问题,所以我在这里记录我的解决方案只是为了后代。
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()
函数使用显示模块模式将目标元素的 id 传递给其scrolling()
函数 via scroll.To('id')
,该函数设置函数使用的值scroller()
。
分解
在scrolling()
:
el
: 目标 DOM 对象elPos
:返回一个函数,通过elememtPosition()
该函数每次调用目标元素相对于页面顶部的位置。duration
: 转换时间,以毫秒为单位。increment
:将目标元素的起始位置分为40步。time
:设置每一步的时间。prev
: 目标元素在scroller()
.E
: 保存目标元素在 中的位置scroller()
。实际工作由scroller()
继续调用自身(通过setTimeout()
)的函数完成,直到目标元素位于页面顶部或页面无法再滚动。
每次scroller()
调用它都会检查目标元素的当前位置(保存在变量中E
),如果它是> 1
OR< -1
并且页面是否仍然可滚动,则将窗口移动increment
像素 - 向上或向下取决于E
是正值还是负值。什么时候E
既不是> 1
OR < -1
,也不是E
===prev
函数停止。我DOMElement.scrollTo()
在完成时添加了该方法,只是为了确保目标元素位于窗口顶部(并不是说您会注意到它超出了一小部分像素!)。
第if
2 行的语句通过检查其先前位置 ( )scroller()
来检查页面是否正在滚动(在目标可能朝向页面底部并且页面无法进一步滚动的情况下)。 E
prev
它下面的三元条件在接近零时减小该increment
值。E
这会阻止页面以一种方式超调,然后弹回以超调另一种方式,然后弹回以再次超调另一种方式,乒乓风格,无穷大甚至更远。
如果您的页面超过 c.4000px 高,您可能希望增加三元表达式的第一个条件(此处为 +/-20)和/或设置该increment
值的除数(此处为 40)中的值。
玩弄duration
,设置的除数increment
,以及三元条件下的值scroller()
应该允许您定制功能以适合您的页面。
NBTested 在 Lubuntu 上的最新版本的 Firefox 和 Chrome 以及 Windows8 上的 Firefox、Chrome 和 IE 中进行测试。
我做了这样的东西。我不知道它是否在 IE8 中工作。在 IE9、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
— 获取元素并检查它是否具有属性“href”,如果是,则去掉“#”elem
——不带“#”的指针变量elemOffset
—“滚动到”元素从页面顶部的偏移量您可以使用
document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});
如果要滚动到页面顶部,只需在顶部放置一个空元素,然后平滑滚动到该元素即可。
<script>
var set = 0;
function animatescroll(x, y) {
if (set == 0) {
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
注意:
为了让 body 滚动,给 body 一个 ID。
JavaScript 中有许多不同的平滑滚动方法。下面列出了最常见的。
要在准确的时间内滚动到某个位置,window.requestAnimationFrame
可以使用,每次计算出合适的当前位置。不支持setTimeout
时可以使用类似的效果。requestAnimationFrame
(要使用以下功能滚动到特定元素,只需将位置设置为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);
}
});
}
演示:
/*
@param time: the exact amount of time the scrolling will take (in milliseconds)
@param pos: the y-position to scroll to (in pixels)
*/
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);
}
});
}
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 300)">
Scroll To Div (300ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 200)">
Scroll To Div (200ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 100)">
Scroll To Div (100ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 50)">
Scroll To Div (50ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 1000)">
Scroll To Div (1000ms)
</button>
<div style="margin: 500px 0px;">
DIV<p/>
<button onClick="scrollToSmoothly(0, 500)">
Back To Top
</button>
<button onClick="scrollToSmoothly(document.body.scrollHeight)">
Scroll To Bottom
</button>
</div>
<div style="margin: 500px 0px;">
</div>
<button style="margin-top: 100px;" onClick="scrollToSmoothly(500, 3000)">
Scroll To y-position 500px (3000ms)
</button>
对于更复杂的情况,可以使用SmoothScroll.js 库,它可以处理垂直和水平平滑滚动、在其他容器元素内滚动、不同的缓动行为、从当前位置相对滚动等等。
var easings = document.getElementById("easings");
for(var key in smoothScroll.easing){
if(smoothScroll.easing.hasOwnProperty(key)){
var option = document.createElement('option');
option.text = option.value = key;
easings.add(option);
}
}
document.getElementById('to-bottom').addEventListener('click', function(e){
smoothScroll({yPos: 'end', easing: easings.value, duration: 2000});
});
document.getElementById('to-top').addEventListener('click', function(e){
smoothScroll({yPos: 'start', easing: easings.value, duration: 2000});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll@1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<!-- Taken from one of the library examples -->
Easing: <select id="easings"></select>
<button id="to-bottom">Scroll To Bottom</button>
<br>
<button id="to-top" style="margin-top: 5000px;">Scroll To Top</button>
或者,您可以传递一个选项对象,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'
});
演示:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
top: elem.offsetTop,
left: 0,
behavior: 'smooth'
});
}
</script>
如果您只需要滚动到一个元素,而不是文档中的特定位置,您可以使用Element.scrollIntoView
with behavior
set to smooth
。
document.getElementById("elemID").scrollIntoView({
behavior: 'smooth'
});
演示:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div id="myDiv" style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
document.getElementById("myDiv").scrollIntoView({
behavior: 'smooth'
});
}
</script>
现代浏览器支持scroll-behavior
CSS 属性,该属性可用于使文档中的滚动平滑(无需 JavaScript)。锚标签可以通过给锚标签 ahref
加上要滚动#
到id
的元素来使用)。您还可以为scroll-behavior
特定容器设置属性,如 adiv
以使其内容平滑滚动。
演示:
html, body{
scroll-behavior: smooth;
}
<a href="#elem">Scroll To Element</a>
<div id="elem" style="margin: 500px 0px;">Div</div>
这是我的解决方案。适用于大多数浏览器
document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});
document.getElementById("end").scrollIntoView({behavior: "smooth"});
body {margin: 0px; display: block; height: 100%; background-image: linear-gradient(red, yellow);}
.start {display: block; margin: 100px 10px 1000px 0px;}
.end {display: block; margin: 0px 0px 100px 0px;}
<div class="start">Start</div>
<div class="end" id="end">End</div>
使用以下平滑滚动可以正常工作:
html {
scroll-behavior: smooth;
}
这是我的变化:
let MenuItem = function ( _menuItem ) {
// I had a sticky header, so its height had to be taken into account when scrolling
let _header = document.querySelector('.site-header');
let _scrollToBlock = function( e, menuItem ) {
let id = menuItem.getAttribute('href'), // the href attribute stores the id of the block to which the scroll will be
headerHeight = _header.offsetHeight; // determine the height of the header
id = id.replace(/#/, ''); // remove the # sign from the id block
let elem = document.getElementById( id ), // define the element to which we will scroll
top = elem.getBoundingClientRect().top + window.scrollY - headerHeight; // determine the height of the scroll
window.scroll({
top: top,
left: 0,
behavior: 'smooth'
});
},
_addEvents = function() {
_menuItem.addEventListener('click', function (e){
e.preventDefault(); // Disable redirect on click
_scrollToBlock(e, _menuItem);
});
},
_init = function() {
_addEvents();
};
_init();
};
// Initialize the class MenuItem to all links with class .menu__item
document.querySelectorAll('.menu__item').forEach( function(item) {
new MenuItem(item);
} );
这是对我有用的代码。
`$('a[href*="#"]')
.not('[href="#"]')
.not('[href="#0"]')
.click(function(event) {
if (
location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
&&
location.hostname == this.hostname
) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
event.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top
}, 1000, function() {
var $target = $(target);
$target.focus();
if ($target.is(":focus")) {
return false;
} else {
$target.attr('tabindex','-1');
$target.focus();
};
});
}
}
});
`