滚动到任意位置
涉及知识点:
window.scrollTo(x, y)
requestAnimationFrame(()=>{}) //帧动画,优点:按浏览器的刷新频率渲染,更加流畅。特点:类似 setTimeout 只执行一次
原生的写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
main {
100vw;
height: 100vh;
overflow: auto;
}
main>.content div {
transition: all 1s ease-in-out;
height: 200px;
border: 1px solid #000;
}
#box1 {
background-color: red;
}
#box2 {
background-color: orange;
}
#box3 {
background-color: yellow;
}
#box4 {
background-color: green;
}
#box5 {
background-color: cyan;
}
#box6 {
background-color: blue;
}
#box7 {
background-color: blueviolet;
}
#but {
outline: none;
position: fixed;
bottom: 30px;
right: 30px;
display: inline-block;
70px;
height: 70px;
line-height: 70px;
border-radius: 50%;
border: 1px solid red;
font-size: 12px;
text-align: center;
z-index: 1000;
}
</style>
</head>
<body>
<main>
<button href="javascript:;" id="but">回到box2</button>
<div class="content">
<div id="box1">1</div>
<div id="box2">2</div>
<div id="box3">3</div>
<div id="box4">4</div>
<div id="box5">5</div>
<div id="box6">6</div>
<div id="box7">7</div>
</div>
</main>
<script>
const but = document.getElementById("but");
but.onclick = () => {
const main = document.querySelector("main"); // 获取滚动的元素(带滚动条)
const toTop = document.getElementById("box2").offsetTop; // 获取需要滚动的高度
smoothscroll(main, toTop)
};
// smoothscroll(需要滚动的元素(默认document), 滚动的最终高度(默认0), 速度(默认1.4))
const smoothscroll = (ele, toTop = 0, speed = 1.4) => {
if (!ele) {
ele = document.documentElement || document.body;
}
if (speed <= 1) {
speed = 1.4
}
let timer = null;
let scrollTo = () => {
const Dvalue = ele.scrollTop - toTop; // 到目标点的距离,正数:需要向上滚,负数:需要向下滚
const absDvalue = Math.abs(Dvalue); // 和目标点的距离
const distance = absDvalue - (absDvalue / speed); // 需要滚动的距离
const onOff = distance < 1; // 判断是否已到位,距离小于1px是,表示已到位
if (Dvalue > 0 && !onOff) { // 向上滚
ele.scrollTop = ele.scrollTop - distance;
timer = window.requestAnimationFrame(scrollTo);
}
if (Dvalue < 0 && !onOff) { // 向下滚
ele.scrollTop = ele.scrollTop + distance;
timer = window.requestAnimationFrame(scrollTo);
}
if (onOff) {
console.log("滚到了")
ele.scrollTop = toTop;
window.cancelAnimationFrame(timer);
};
};
scrollTo();
};
</script>
</body>
</html>
react 版代码:
num 是需要到达的位置, 数字类型。调用:this.transitionScroll(100)
下面这段代码,只需要知道,每个索引对应的高度就可以了。
let timer = null;
this.state = {
onOff: true, //初始值为true,默认可以点击,在滚动的时候变成false,结束滚动再次为true
active: 0
}
headerBtnClick = (index) => { //点击改变索引
if(this.state.onOff) {
this.setState({
active: index
}, () => {
this.scrollFn(index)
})
}
}
scrollFn = (index) => { //每个索引对应的位置
//this.transitionScroll(...) 根据索引,判断需要滚动到的位置。
}
componentDidMount() {
window.addEventListener("scroll", () => { //监听是否为滚动状态,在滚动状态不可再次点击
clearTimeout(timer) //利用防抖技巧,在结束滚动250ms之后恢复可点击状态
timer = setTimeout(() => {
this.setState({
onOff: true
})
}, 250)
this.setState({
onOff: false
})
})
}
componentWillUnmount() {
if(timer) {
clearTimeout(timer)
}
}
//sensitivity: 容错率和灵敏度为 5 时正好
//容错率越小,滚动的位置越精确,太小可能会出现无限回调的错误(主要原因是数字相除再取整,值不再精确)
transitionScroll = (num) => { //判断需要上滚,还是下滚
const n = window.pageYOffset;
if(num < n) {
this.ScrollDown(num)
}
if(num > n) {
this.ScrollUp(num)
}
}
ScrollDown = (num) => { //向下滚
const sensitivity = 5;
window.requestAnimationFrame(() => {
const n = window.pageYOffset;
const Dvalue = Math.abs(num - n);
if (num > n + sensitivity) {//容错率
window.scrollTo(0, n + parseInt(Dvalue / sensitivity))//灵敏度
this.ScrollDown(num)
}
})
}
ScrollUp = (num) => { //向上滚
const sensitivity = 5;
window.requestAnimationFrame(() => {
const n = window.pageYOffset;
const Dvalue = Math.abs(num - n);
if (num < n - sensitivity) {
window.scrollTo(0, n - parseInt(Dvalue / sensitivity))
this.ScrollDown(num)
}
})
}