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

相关文章

Spring Boot 3.x 中 WebClient 示例详解析

《SpringBoot3.x中WebClient示例详解析》SpringBoot3.x中WebClient是响应式HTTP客户端,替代RestTemplate,支持异步非阻塞请求,涵盖GET... 目录Spring Boot 3.x 中 WebClient 全面详解及示例1. WebClient 简介2.

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

C#解析JSON数据全攻略指南

《C#解析JSON数据全攻略指南》这篇文章主要为大家详细介绍了使用C#解析JSON数据全攻略指南,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、为什么jsON是C#开发必修课?二、四步搞定网络JSON数据1. 获取数据 - HttpClient最佳实践2. 动态解析 - 快速

Spring Boot3.0新特性全面解析与应用实战

《SpringBoot3.0新特性全面解析与应用实战》SpringBoot3.0作为Spring生态系统的一个重要里程碑,带来了众多令人兴奋的新特性和改进,本文将深入解析SpringBoot3.0的... 目录核心变化概览Java版本要求提升迁移至Jakarta EE重要新特性详解1. Native Ima

spring中的@MapperScan注解属性解析

《spring中的@MapperScan注解属性解析》@MapperScan是Spring集成MyBatis时自动扫描Mapper接口的注解,简化配置并支持多数据源,通过属性控制扫描路径和过滤条件,利... 目录一、核心功能与作用二、注解属性解析三、底层实现原理四、使用场景与最佳实践五、注意事项与常见问题六

nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析(结合应用场景)

《nginx-t、nginx-sstop和nginx-sreload命令的详细解析(结合应用场景)》本文解析Nginx的-t、-sstop、-sreload命令,分别用于配置语法检... 以下是关于 nginx -t、nginx -s stop 和 nginx -s reload 命令的详细解析,结合实际应

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

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