本文主要是介绍苹果官网动画解析之airpods滚动光影效果,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
每次看到苹果官网的动画效果都令人震惊,总想去理解他并复刻下来,那我们今天就来实现苹果官网中AirPods的滚动变化光影的效果。
下面先看最终的效果:
airpods滚动效果
可以看出,我们向下滚动的时候,图片元素并没有跟随滚动,光影效果一直在改变。如果我们向上滚动,则会让动画效果反方向运行。这就是我们想要的结果。
核心原理
根据滚动,使用Canvas的drawImage不停的画不同的图片,从而实现不同的展示效果
准备工作
1. 图片哪里来?
苹果有一组图片:
https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/xxx.jpg
xxx: 从0001到 0147
滚动怎么监听?原生写?
当然不是,这里我们使用gsap这个库。
简单来说,这就是一个使用js
创建动画的库,好用之处在于它支持很多插件,并且可以很方便的使用属性来控制css
样式。缺点就是有些插件是要收费的,但是我们今天用到的 ScrollTrigger
是免费的。
这里就不介绍具体使用方法,因为太多,可以自己去官方网站看看。
正式开始
样式使用的是Tailwind CSS,这个用习惯了还是很好用的。
1. 创建一个空的canvas并引入gsap
<section class="airpods bg-black py-14"><canvas id="airpods"></canvas>
</section><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.4/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.4/ScrollTrigger.min.js"></script>
2. 使用canvas画第一张图
const canvas = document.getElementById('airpods');const context = canvas.getContext("2d");// 这里根据图片的大小设置宽高canvas.width = 1158;canvas.height = 770;const img = new Image();img.src = 'https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/0001.jpg';img.onload = () => {context.drawImage(img, 0, 0, canvas.width, canvas.height);};
3. 预加载所有图片,以便动画能够流畅
const frameCount = 147;function preloadImages() { for (let i = 1; i < frameCount; i++) {const img = new Image();img.src = currentFrame(i);}}// 处理图片地址function currentFrame(index) { return `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${index.toString().padStart(4, "0")}.jpg`;}preloadImages();
4. 使用gsap创建动画
// 要使用插件,必须先注册gsap.registerPlugin(ScrollTrigger);/** gsap语法:* gsap.to(target, {property: value, property: value, ...});** scrollTrigger语法:* scrollTrigger: {* trigger: target, // 滚动谁触发动画* scrub: true | false, // 动画是否重复执行,也就是再次进入后触发与否* pin: true | false, // 是否固定在触发位置* start: "top 10%", // 触发开始位置 后面详细介绍* end: "bottom+=300% 10%", // 触发结束位置* markers: true | false, // 是否显示辅助线,主要用于开发阶段* ...* }* **/gsap.to(canvas, {scrollTrigger: {trigger: canvas,scrub: true,start: "top 10%",end: "bottom+=300% 10%",pin: true,markers: true,onUpdate: (self) => {// self.progress 表示滚动的百分比 0-1const frameIndex = Math.min(frameCount,Math.ceil(self.progress * frameCount + 1));requestAnimationFrame(() => updateImage(frameIndex));},}});function updateImage(index) {img.src = currentFrame(index);context.drawImage(img, 0, 0);}
完成上面几步就能够实现我们最终的效果了。
start、end详细介绍
我认为其他参数都还是比较好理解,唯独这两个有一些绕,所以这里单独介绍下。
首先看默认值
首先要清楚这两个值由两部分组成,分别控制元素和视口。
只要
start
在scroller-start
之上,且end
在scroller-end
之下的,那么动画就会触发。
这两部分的值可以有很多种形式,比如单词类:top
、bottom
、center
,百分比10%
、100%
, 像素 100px
等等,他们之间可以使用相对位置,比如top+=10%
,bottom-=100px
等等。
所以这个部分不同的组合就能创建出不同的触发动画的时机,只有慢慢去尝试,才能理解得非常清晰。
就拿我们的代码来说:
start: "top 10%",end: "bottom+=300% 10%",
end 不见了是因为它在视口以外了。
这个end为什么是300%?
因为我们需要滚动时拉长总的滚动距离(分母),而鼠标滚动一次的距离(分子)几乎是固定的,这样就让我们的progress
变得更加小,从而使动画更加平滑。
progress = 拉长总的滚动距离 / 鼠标滚动一次的距离
完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><title>AirPods</title>
</head>
<body><main class="bg-slate-300 animate-demo "><section class="airpods bg-black py-14"><canvas id="airpods" class=""></canvas></section></main><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.4/gsap.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.4/ScrollTrigger.min.js"></script><script>function airpods() {const frameCount = 147;const canvas = document.getElementById('airpods');const context = canvas.getContext("2d");canvas.width = 1158;canvas.height = 770;const img = new Image();img.src = currentFrame(1);img.onload = () => {context.drawImage(img, 0, 0, canvas.width, canvas.height);};/** gsap语法:* gsap.to(target, {property: value, property: value, ...});** scrollTrigger语法:* scrollTrigger: {* trigger: target, // 滚动谁触发动画* scrub: true | false, // 动画是否重复执行,也就是再次进入后触发与否* pin: true | false, // 是否固定在触发位置* start: "top 10%", // 触发开始位置 后面详细介绍* end: "bottom+=300% 10%", // 触发结束位置* markers: true | false, // 是否显示辅助线,主要用于开发阶段* ...* }* **/gsap.to(canvas, {scrollTrigger: {trigger: canvas,scrub: true,start: "top 10%",end: "bottom+=300% 10%",pin: true,markers: true,onUpdate: (self) => {// self.progress 表示滚动的百分比 0-1const frameIndex = Math.min(frameCount,Math.ceil(self.progress * frameCount + 1));requestAnimationFrame(() => updateImage(frameIndex));},}});function updateImage(index) {img.src = currentFrame(index);context.drawImage(img, 0, 0);}function preloadImages() {for (let i = 1; i < frameCount; i++) {const img = new Image();img.src = currentFrame(i);}}function currentFrame(index) {return `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${index.toString().padStart(4, "0")}.jpg`;}preloadImages();}airpods();</script>
</body>
</html>
拿去就能跑,你可以试试~
如果你觉得有用,欢迎评论、点赞、转发~
当然也希望你能关注我的公众号:前端大乱炖
这篇关于苹果官网动画解析之airpods滚动光影效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!