项目:我已经设置了一个滚动功能,当单击“向上”或“向下”时,它应该滚动到容器 div 中的下一个项目。它利用 window.requestAnimationFrame() 方法进行循环并在每次迭代时调用 element.scrollTo。
我这样做的原因是允许自定义持续时间,因为“行为:'平滑'”设置是一个设定的速度,我想保留 css 滚动捕捉功能,所以它需要在一个容器内(所以窗口.scrollTo() 是不可能的,尽管这似乎适用于浏览器)。
问题:这在 Safari 中完美运行,但由于某种原因 .scrollTo() 函数没有在 Chrome 和 Firefox 中移动滚动视图。
我试过的:
- 我尝试设置 element.scrollTop 而不是调用 element.scrollTo() 具有相同的结果。
- 我在某处读到 Chrome 的性能对于某些循环来说太快了,所以我用不同持续时间的 setTimeout(()=>{},1) 包围了 element.scrollTo()。这也不起作用。
我的代码:代码可在 Codepen.io 中查看
HTML:
<div class="scroll-wrap">
<div id="item-1" class="scroll-item blue">
<h1>1</h1>
<div class="scroll-up"></div>
<div class="scroll-down"></div>
</div>
<div id="item-2" class="scroll-item green">
<h1>2</h1>
<div class="scroll-up"></div>
<div class="scroll-down"></div>
</div>
<div id="item-3" class="scroll-item red">
<h1>3</h1>
<div class="scroll-up"></div>
<div class="scroll-down"></div>
</div>
</div>
JS:
const scrollUps = document.querySelectorAll('.scroll-up')
const scrollDowns = document.querySelectorAll('.scroll-down')
// options...
options = {
scrollWrap: document.querySelector('.scroll-wrap'), // scroll container.
duration: 300, // Pixels per second. The smaller the number the longer the duration.
easing: 'easeInOutSine', // can be easeOutSine, easeInOutSine, easeInOutQuint.
}
// RAF shim from ... http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// Click Scroll function
function scrollToY(currentPosition, scrollTargetY) {
// currentPostion: the current scrollY property of the view within the scroll container
// scrollTargetY: the target scrollY property of the window
// speed: time in pixels per second
// easing: easing equation to use
var scrollY = currentPosition,
scrollTargetY = scrollTargetY || 0,
speed = options.duration || 2000,
easing = options.easing || 'easeOutSine',
currentTime = 0;
// *For speed in pixels per second...
// min time .1, max time 5 seconds
var time = Math.max(.1, Math.min(Math.abs(scrollY - scrollTargetY) / speed, 5));
// easing equations from https://github.com/danro/easing-js/blob/master/easing.js
const PI_D2 = Math.PI / 2,
easingEquations = {
easeOutSine: function (pos) {
return Math.sin(pos * (Math.PI / 2));
},
easeInOutSine: function (pos) {
return (-0.5 * (Math.cos(Math.PI * pos) - 1));
},
easeInOutQuint: function (pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 5);
}
return 0.5 * (Math.pow((pos - 2), 5) + 2);
}
};
// animation loop
function tick() {
currentTime += 1 / 60;
const p = currentTime / time;
const t = easingEquations[easing](p);
if (p < 1) {
requestAnimFrame(tick);
const newPosition = scrollY + ((scrollTargetY - scrollY) * t)
setTimeout(()=>{
options.scrollWrap.scrollTo(0, newPosition);
// console.log('scroll:', options.scrollWrap.scrollTop);
},1)
} else {
// console.log('scroll done');
options.scrollWrap.scrollTo(0, scrollTargetY);
}
}
// call it once to get started
tick();
}
// set clickable areas...
Array.from(scrollUps).forEach(btn => {
btn.addEventListener('click', (e) => {
// Get Parent of button...
const parent = e.target.parentElement
// Get destination element...
const destId = `item-${Number(parent.id.split('-')[1]) - 1}`
const dest = document.getElementById(destId)
// call scroll function if destination exists...
if(dest) {
const destDistanceToTop = dest.offsetTop
scrollToY(parent.offsetTop, destDistanceToTop)
}
})
})
Array.from(scrollDowns).forEach(btn => {
btn.addEventListener('click', (e) => {
const parent = e.target.parentElement
const destId = `item-${Number(parent.id.split('-')[1]) + 1}`
const dest = document.getElementById(destId)
if(dest) {
const destDistanceToTop = dest.offsetTop
// console.log(destDistanceToTop, parent.offsetTop)
scrollToY(parent.offsetTop, destDistanceToTop)
}
})
})