苹果官网动画解析之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

相关文章

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实