背景:前几天在做项目的过程中需要更改 div 的 scrollTop, 要求运动过程类似 IOS Webview 可视区吸顶动画,先快后慢
分析:
- css 不能设置 offsetTop,需要 JS 动态更改 offsetTop。
- 动画先快后慢意味着,scrollTop 更改的速度越来越慢
- 移动总体的距离是 scrollTop。
- 事件控制在两秒之类,小于一屏幕 1s,大于一屏幕 2s
- 速度??距离??速度在时间上的积分等于距离????定积分!!!!
猜想函数:
什么函数是递减的??
什么函数做定积分好做。换句话说,原函数好找??
什么函数作用域包涵 0(因为需要从 0 时刻开始)
什么函数在 0 -2S 的积分为 scrollTop
盲猜 y = 1 / x;
绘图地址
作用域不包含 0???向右平移一个单位???左加右减,上加下减!!!哈哈哈哈 高数老师的敦敦教导 = =
y = 1 / (x + 1);
y = 1 / (x + 1); 在 0 -2s 的积分一定是 scrollTop 吗????
需要一个系数????
y = K / (x + 1) ;
怎么求出这个系数呢??
y = K / (x + 1);
y 在 0 – 2 上的积分 = scrollTop;
y = 1 / x 的原函数 f(x) = Math.log(x) + c,c 为常数;
类推求出 y = K / (x + 1)的原函数 f(x) = Math.log(x + 1) + c, c 为常数;
最终实现代码如下:
优化性能
setInterval 实现这种效果只是使用 20ms 作为 offset 更改的时间点
更高的优化????requestAnimFrame!!!MDN requestAnimationFrame
核心思路:获取两次递归的事件间隔???new Date().getTime() – startTime
最终实现如下:
完整代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title> 测试 scroll</title>
<style>
* {
padding: 0;
margin: 0;
}
.scroll {
height: 400px;
overflow: scroll;
width: 100px;
background-color: red;
}
.scroll div:nth-child(even) {
height: 60px;
background-color: red;
}
.scroll div:nth-child(odd) {
height: 60px;
background-color: green;
}
.button {
position: fixed;
right: 0;
top: 0;
height: 30px;
width: 100px;
background-color: red;
font-size: 12px;
line-height: 30px;
text-align: center;
}
</style>
<script>
window.addEventListener('load', () => {const scrollDOM = document.querySelector('.scroll');
const btnDOM = document.querySelector('.button');
window.requestAnimFrame = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame;
btnDOM.addEventListener('click', () => {console.log(scrollDOM.scrollTop);
let scrollTop = scrollDOM.scrollTop;
window.requestAnimFrame
? window.requestAnimFrame(() => requestFrameUpdateScrollTop(new Date().getTime(), scrollTop))
: setTimeUpdateScrollTop(scrollTop);
});
const requestFrameUpdateScrollTop = (startTime, scrollTop) => {
const timeStep = 20;
// 总时间 2000 ms
const allX = 2000 / timeStep;
let startX = 0;
// 系数
const ratio = scrollTop / Math.log(allX + 1);
// 定积分可得
const speed = ratio * (1 / (startX + 1));
const time = new Date().getTime();
const timeDiff = time - startTime;
startX += Math.floor(timeDiff / timeStep) + 1;
scrollTop = scrollTop - speed * startX;
scrollTop = scrollTop <= 1 ? 0 : scrollTop;
scrollDOM.scrollTop = scrollTop;
if (scrollTop > 0) {window.requestAnimFrame(() => requestFrameUpdateScrollTop(Date.now(), scrollTop));
}
};
const setTimeUpdateScrollTop = scrollTop => {if (this.timer) {return;}
const timeStep = 20;
// 总时间 2000 ms
const allX = 2000 / timeStep;
let startX = 0;
// 系数
const ratio = scrollTop / Math.log(allX + 1);
// 定积分可得
this.timer = setInterval(() => {const speed = ratio * (1 / (startX + 1));
console.log(speed);
startX++;
scrollTop = scrollTop - speed;
scrollDOM.scrollTop = scrollTop < 0 ? 0 : scrollTop;
if (scrollTop <= 0) {clearInterval(this.timer);
this.timer = null;
}
}, timeStep);
};
});
</script>
</head>
<body>
<div class="scroll">
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
<div>asdasd</div>
</div>
<div class="button">
开始 123123123 运动
</div>
</body>
</html>
这是先快后慢的动画,其他动画的解决思路同理
核心:
找函数,做积分!!!找函数,做积分!!!找函数,做积分!!!
最后说句:如果能用 css 实现,少用 JS