苹果官网动画解析之airpods滚动光影效果

2024-01-16 20:04

本文主要是介绍苹果官网动画解析之airpods滚动光影效果,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

每次看到苹果官网的动画效果都令人震惊,总想去理解他并复刻下来,那我们今天就来实现苹果官网中AirPods的滚动变化光影的效果。

下面先看最终的效果:

airpods滚动效果

可以看出,我们向下滚动的时候,图片元素并没有跟随滚动,光影效果一直在改变。如果我们向上滚动,则会让动画效果反方向运行。这就是我们想要的结果。

核心原理

根据滚动,使用CanvasdrawImage不停的画不同的图片,从而实现不同的展示效果

准备工作

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详细介绍

我认为其他参数都还是比较好理解,唯独这两个有一些绕,所以这里单独介绍下。

首先看默认值

在这里插入图片描述

首先要清楚这两个值由两部分组成,分别控制元素视口

只要startscroller-start之上,且endscroller-end之下的,那么动画就会触发。

这两部分的值可以有很多种形式,比如单词类:topbottomcenter,百分比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滚动光影效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/613738

相关文章

MySQL中FIND_IN_SET函数与INSTR函数用法解析

《MySQL中FIND_IN_SET函数与INSTR函数用法解析》:本文主要介绍MySQL中FIND_IN_SET函数与INSTR函数用法解析,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一... 目录一、功能定义与语法1、FIND_IN_SET函数2、INSTR函数二、本质区别对比三、实际场景案例分

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

关于WebSocket协议状态码解析

《关于WebSocket协议状态码解析》:本文主要介绍关于WebSocket协议状态码的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录WebSocket协议状态码解析1. 引言2. WebSocket协议状态码概述3. WebSocket协议状态码详解3

CSS Padding 和 Margin 区别全解析

《CSSPadding和Margin区别全解析》CSS中的padding和margin是两个非常基础且重要的属性,它们用于控制元素周围的空白区域,本文将详细介绍padding和... 目录css Padding 和 Margin 全解析1. Padding: 内边距2. Margin: 外边距3. Padd

Oracle数据库常见字段类型大全以及超详细解析

《Oracle数据库常见字段类型大全以及超详细解析》在Oracle数据库中查询特定表的字段个数通常需要使用SQL语句来完成,:本文主要介绍Oracle数据库常见字段类型大全以及超详细解析,文中通过... 目录前言一、字符类型(Character)1、CHAR:定长字符数据类型2、VARCHAR2:变长字符数

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI