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

拿去就能跑,你可以试试~

如果你觉得有用,欢迎评论、点赞、转发~
当然也希望你能关注我的公众号:前端大乱炖
在这里插入图片描述

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Minecraft光影开发是指对游戏中的光照效果进行改变和优化的过程。光影效果可以使游戏更加逼真和具有视觉冲击力。 光影效果在Minecraft中是通过修改游戏的渲染引擎和加入特定的光影模组或纹理包来实现的。开发者需要了解游戏渲染引擎的运作原理以及使用合适的工具和编程语言来进行开发。 在光影效果的开发中,主要关注以下几个方面: 1. 光源效果:通过修改光源的亮度、颜色和投射的光线方向来实现更加真实的光照效果。可以通过改变阴影的长度和角度来模拟阳光的折射效果。 2. 环境光效果:通过调整游戏世界的环境颜色、亮度和色调来改变整体的光影效果。可以模拟日出、日落、阴雨天等不同的自然光照条件。 3. 材质效果:通过修改方块和物品的材质贴图以及光影效果来实现更加真实的渲染效果。可以通过使用法线贴图、置换贴图等技术来增强材质的立体感和细节。 4. 着色器效果:通过编写着色器程序,实现一些高级的光影效果,如全局光照、体积光、光线折射等。着色器可以对图像进行实时计算和处理,使得光影效果更加逼真。 开发Minecraft光影效果需要有一定的编程知识和图形学基础,同时需要对Minecraft游戏的渲染原理有深入的了解。开发者可以参考已有的光影模组和纹理包进行学习和实践,也可以加入相关的开发社区获取更多资源和经验。 总之,Minecraft光影开发是一个挑战性的任务,通过优化光照效果可以让游戏的视觉效果更加出众,丰富了玩家的游戏体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yanyi24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值