各位靓仔靓女们,早上好/下午好/晚上好!我是你们的滚动大师,今天咱们来聊聊如何用 requestAnimationFrame
把网页滚动玩出花,做出丝滑般顺畅的动画效果。准备好了吗?Let’s roll!
第一节:requestAnimationFrame
是个啥玩意儿?
首先,requestAnimationFrame
这玩意儿可不是让你去申请动画基金的(虽然我也想),它是一个浏览器提供的 API,专门用来做动画。
想象一下,你正在看一部电影,电影是由一帧一帧的画面快速播放形成的。我们的网页动画也是一样的道理,我们需要不停地更新画面,才能让它动起来。
requestAnimationFrame
的作用就是告诉浏览器:“嘿,老兄,我这里有个动画要搞,你能不能在下一次屏幕刷新之前,帮我执行一下这个函数?”
浏览器会根据你的显示器的刷新率(比如 60Hz,也就是每秒刷新 60 次)来安排这个函数的执行时间。这样一来,你的动画就能跟上浏览器的节奏,避免卡顿和掉帧。
简单来说,requestAnimationFrame
就像一个闹钟,到点就叫你起来更新动画,而且这个闹钟是浏览器亲自设定的,保证准时准点。
第二节:为什么要用 requestAnimationFrame
?
你可能会问:“我用 setInterval
或者 setTimeout
不也能做动画吗?干嘛非要用 requestAnimationFrame
这么个拗口的东西?”
问得好!setInterval
和 setTimeout
的确可以用来做动画,但它们有几个缺点:
- 时间不精确:
setInterval
和setTimeout
的执行时间并不保证精确,可能会受到其他代码的影响,导致动画卡顿。 - 浪费资源: 就算页面不可见(比如切换到其他标签页),
setInterval
和setTimeout
仍然会继续执行,浪费 CPU 资源。 - 不同步: 它们无法与浏览器的刷新率同步,可能会出现掉帧或者撕裂现象。
而 requestAnimationFrame
则完美地解决了这些问题:
- 同步刷新: 它会与浏览器的刷新率同步,保证动画流畅。
- 节省资源: 当页面不可见时,
requestAnimationFrame
会自动停止执行,节省 CPU 资源。 - 高性能: 浏览器会对
requestAnimationFrame
进行优化,使其执行效率更高。
总之,requestAnimationFrame
是做网页动画的不二之选。它就像一个专业的舞蹈演员,能完美地配合浏览器的节奏,跳出优雅的动画。
第三节:requestAnimationFrame
的基本用法
requestAnimationFrame
的用法非常简单:
function animate() {
// 在这里更新动画
console.log("动画正在运行...");
// 再次调用 requestAnimationFrame,形成循环
requestAnimationFrame(animate);
}
// 启动动画
requestAnimationFrame(animate);
这段代码会不停地输出 "动画正在运行…",直到你手动停止它。
让我们来分解一下这段代码:
animate
函数: 这个函数是动画的核心,你需要在里面更新动画的属性。requestAnimationFrame(animate)
: 这行代码会告诉浏览器,在下一次屏幕刷新之前,执行animate
函数。- 循环调用: 在
animate
函数的末尾,我们再次调用requestAnimationFrame(animate)
,形成一个循环,让动画持续运行。
就像贪吃蛇游戏一样,蛇头每移动一次,就要再次发出移动指令,才能让它继续前进。
第四节:用 requestAnimationFrame
实现平滑滚动
好了,现在我们来用 requestAnimationFrame
实现一个平滑滚动的效果。
假设我们有一个页面,想要点击一个按钮,让页面平滑地滚动到某个位置。
首先,我们需要获取按钮和目标位置的元素:
<button id="scrollToTopBtn">滚动到顶部</button>
<div style="height: 2000px;"></div> <!-- 模拟页面内容 -->
<div id="targetElement" style="position: absolute; top: 0; left: 0;">顶部</div>
const scrollToTopBtn = document.getElementById("scrollToTopBtn");
const targetElement = document.getElementById("targetElement");
接下来,我们需要一个函数来执行滚动动画:
function scrollToTop(duration) {
const start = window.pageYOffset; // 滚动开始时的位置
const target = targetElement.offsetTop; // 目标位置
const change = target - start; // 总的滚动距离
let startTime = null;
function animateScroll(currentTime) {
if (startTime === null) {
startTime = currentTime;
}
const timeElapsed = currentTime - startTime;
const run = ease(timeElapsed, start, change, duration); // 计算当前滚动位置
window.scrollTo(0, run);
if (timeElapsed < duration) {
requestAnimationFrame(animateScroll); // 继续滚动
} else {
// 滚动结束
window.scrollTo(0, target); // 确保滚动到精确位置
}
}
requestAnimationFrame(animateScroll);
}
让我们来分析一下这个函数:
scrollToTop(duration)
: 这个函数接受一个参数duration
,表示滚动动画的持续时间(毫秒)。start
: 滚动开始时的位置,也就是当前页面的滚动距离。target
: 目标位置,也就是targetElement
的顶部距离页面顶部的距离。change
: 总的滚动距离,也就是target - start
。startTime
: 动画开始的时间,用于计算已经过去的时间。animateScroll(currentTime)
: 这个函数是动画的核心,它会根据当前时间计算出滚动位置,并更新页面的滚动距离。ease(timeElapsed, start, change, duration)
: 这是一个缓动函数,用于控制动画的速度。我们稍后会详细讲解。window.scrollTo(0, run)
: 这行代码会更新页面的滚动距离,让页面滚动到指定的位置。requestAnimationFrame(animateScroll)
: 这行代码会继续执行滚动动画,直到滚动到目标位置。- 滚动结束时
window.scrollTo(0, target)
:这行代码为了保证精确的到达目标位置,因为easing 函数计算出来的值,可能存在一定的误差。 currentTime
:requestAnimationFrame
会自动传入一个参数,表示当前时间。
最后,我们需要给按钮添加点击事件监听器:
scrollToTopBtn.addEventListener("click", function() {
scrollToTop(1000); // 滚动时间为 1 秒
});
现在,当你点击按钮时,页面就会平滑地滚动到顶部。
第五节:缓动函数(Easing Functions)
你可能已经注意到了,在 scrollToTop
函数中,我们使用了一个 ease
函数来控制动画的速度。这个函数叫做缓动函数,它可以让动画看起来更加自然和流畅。
如果没有缓动函数,动画的速度会是线性的,也就是匀速运动。这样的动画看起来会比较生硬。
缓动函数可以改变动画的速度,让它在开始时慢,中间快,结束时慢,或者其他各种各样的效果。
这里我们提供一个常用的缓动函数:
function ease(t, b, c, d) {
t /= d / 2;
if (t < 1) return c / 2 * t * t + b;
t--;
return -c / 2 * (t * (t - 2) - 1) + b;
}
这个函数实现的是一个 "easeInOutQuad" 缓动效果,也就是在开始和结束时速度较慢,中间速度较快。
让我们来解释一下这个函数的参数:
t
: 当前时间(已经过去的时间)。b
: 初始值(滚动开始时的位置)。c
: 总的改变值(总的滚动距离)。d
: 持续时间(滚动动画的持续时间)。
这个函数会根据这些参数计算出当前应该滚动到的位置。
当然,你也可以使用其他的缓动函数,比如 "easeInQuad"、"easeOutQuad"、"easeInOutCubic" 等等。网上有很多缓动函数的代码可以找到。
第六节:更复杂的动画效果
除了平滑滚动,requestAnimationFrame
还可以用来实现各种各样的动画效果,比如:
- 元素淡入淡出: 改变元素的透明度,让它逐渐显示或隐藏。
- 元素移动: 改变元素的位置,让它从一个地方移动到另一个地方。
- 元素缩放: 改变元素的大小,让它逐渐放大或缩小。
- 元素旋转: 改变元素的角度,让它旋转。
这些动画效果的原理都是一样的:
- 定义动画的属性: 比如透明度、位置、大小、角度等等。
- 定义动画的起始值和结束值: 比如透明度从 0 到 1,位置从 (0, 0) 到 (100, 100) 等等。
- 使用
requestAnimationFrame
定期更新动画的属性: 根据时间计算出当前应该更新到的值,并应用到元素上。 - 使用缓动函数控制动画的速度: 让动画看起来更加自然和流畅。
第七节:使用第三方库简化动画开发
如果你觉得手动编写动画代码太麻烦,可以使用一些第三方库来简化动画开发。
比较流行的动画库有:
- GreenSock Animation Platform (GSAP): 一个功能强大的动画库,可以实现各种复杂的动画效果。
- Anime.js: 一个轻量级的动画库,易于使用,适合简单的动画效果。
- Velocity.js: 一个高性能的动画库,可以实现流畅的动画效果。
这些库都提供了丰富的 API 和预设的动画效果,可以让你快速地创建出漂亮的动画。
第八节:性能优化
虽然 requestAnimationFrame
已经做了很多优化,但我们仍然需要注意一些性能问题,以保证动画的流畅性:
- 避免在动画中进行大量的计算: 尽量把计算放在动画之外,或者使用 Web Workers 将计算放到后台线程中。
- 减少 DOM 操作: 频繁的 DOM 操作会导致页面重绘和回流,影响动画的性能。尽量使用 CSS transform 来改变元素的位置和大小。
- 使用硬件加速: 某些 CSS 属性(比如
transform
和opacity
)可以触发硬件加速,提高动画的性能。 - 避免内存泄漏: 在动画结束时,记得清理掉不再需要的资源,比如事件监听器和定时器。
第九节:总结
今天我们学习了如何使用 requestAnimationFrame
实现平滑滚动和动画效果。
requestAnimationFrame
是一个强大的 API,可以让你创建出流畅、高性能的网页动画。
希望大家能够掌握 requestAnimationFrame
的用法,并在实际项目中应用它,让你的网页更加生动有趣!
最后,给大家留个小作业:
尝试使用 requestAnimationFrame
实现一个元素淡入淡出的动画效果。
祝大家学习愉快!下次再见!
表格:requestAnimationFrame
vs setInterval
/ setTimeout
特性 | requestAnimationFrame |
setInterval / setTimeout |
---|---|---|
执行时机 | 与浏览器刷新同步 | 不精确 |
资源消耗 | 页面不可见时暂停执行 | 持续执行 |
性能 | 浏览器优化 | 性能较差 |
动画流畅性 | 流畅 | 可能卡顿 |
用途 | 动画 | 定时任务 |
代码示例:淡入淡出动画
<div id="fadeElement" style="opacity: 0; width: 100px; height: 100px; background-color: red;"></div>
<button id="fadeBtn">淡入</button>
<script>
const fadeElement = document.getElementById("fadeElement");
const fadeBtn = document.getElementById("fadeBtn");
let opacity = 0;
let fadingIn = true;
function fadeInOut() {
if (fadingIn) {
opacity += 0.01;
if (opacity >= 1) {
opacity = 1;
fadingIn = false;
}
} else {
opacity -= 0.01;
if (opacity <= 0) {
opacity = 0;
fadingIn = true;
}
}
fadeElement.style.opacity = opacity;
requestAnimationFrame(fadeInOut);
}
fadeBtn.addEventListener("click", function() {
requestAnimationFrame(fadeInOut);
});
</script>
这个例子展示了一个简单的淡入淡出效果。点击按钮后,红色方块会逐渐显示,然后逐渐消失,循环往复。代码也比较容易理解,大家可以自己尝试修改参数,体验不同的动画效果。