使用CSS和GSAP创建3D滚动驱动文本动画

news/2025/11/7 22:25:32/文章来源:https://www.cnblogs.com/qife122/p/19201058

使用CSS和GSAP创建3D滚动驱动文本动画

本教程将指导您使用CSS、JavaScript和GSAP构建三种滚动驱动的文本效果。无需依赖3D库,您将结合CSS变换与GSAP的ScrollTrigger插件,直接将运动与滚动位置关联,创建流畅、高性能的3D动画。

初始设置

第一步是初始化项目并设置其结构。我们将使用基于类的模型,从App类作为主入口点开始,并为每个动画使用三个独立的类。

核心是GSAP。我们将注册其ScrollTrigger和ScrollSmoother插件,这两个插件处理所有三种效果中的平滑滚动和基于滚动的动画。

主入口点将是main.ts文件:

import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { ScrollSmoother } from "gsap/ScrollSmoother";class App {smoother!: ScrollSmoother;constructor() {gsap.registerPlugin(ScrollTrigger, ScrollSmoother);this.init();this.addEventListeners();}init(): void {this.setupScrollSmoother();}setupScrollSmoother(): void {this.smoother = ScrollSmoother.create({smooth: 1,effects: true,});}addEventListeners(): void {window.addEventListener("resize", () => {console.log("resize");});}
}new App();

第一种效果:圆柱体

对于第一种效果,我们将文本围绕一个不可见的圆柱体定位,随着滚动而显示。

使用HTML和CSS构建结构

<section class="cylinder__wrapper"><p class="cylinder__title">keep scrolling to see the animation</p><ul class="cylinder__text__wrapper"><li class="cylinder__text__item">design</li><li class="cylinder__text__item">development</li><!-- 更多列表项 --></ul>
</section>
.cylinder__wrapper {width: 100%;height: 100svh;position: relative;perspective: 70vw;overflow: hidden;display: flex;flex-direction: column;justify-content: center;align-items: center;gap: 10rem;
}.cylinder__text__wrapper {position: absolute;font-size: 5vw;transform-style: preserve-3d;transform-origin: center center;
}.cylinder__text__item {position: absolute;left: 50;top: 50%;width: 100%;backface-visibility: hidden;
}

使用JavaScript实现动态效果

我们创建一个Cylinder类:

import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';export class Cylinder {title: HTMLElement;textWrapper: HTMLElement;textItems: NodeListOf<HTMLElement>;wrapper: HTMLElement;constructor() {this.title = document.querySelector('.cylinder__title') as HTMLElement;this.textWrapper = document.querySelector('.cylinder__text__wrapper') as HTMLElement;this.textItems = document.querySelectorAll('.cylinder__text__item') as NodeListOf<HTMLElement>;this.wrapper = document.querySelector('.cylinder__wrapper') as HTMLElement;this.init();}init() {this.calculatePositions();this.createScrollTrigger();}calculatePositions(): void {const offset = 0.4;const radius = Math.min(window.innerWidth, window.innerHeight) * offset;const spacing = 180 / this.textItems.length;this.textItems.forEach((item, index) => {const angle = (index * spacing * Math.PI) / 180;const rotationAngle = index * -spacing;const x = 0;const y = Math.sin(angle) * radius;const z = Math.cos(angle) * radius;item.style.transform = `translate3d(-50%, -50%, 0) translate3d(${x}px, ${y}px, ${z}px) rotateX(${rotationAngle}deg)`;});}createScrollTrigger(): void {ScrollTrigger.create({trigger: this.title,start: "center center",end: "+=2000svh",pin: this.wrapper,scrub: 2,animation: gsap.fromTo(this.textWrapper,{ rotateX: -80 },{ rotateX: 270, ease: "none" }),});}resize(): void {this.calculatePositions();}
}

第二种效果:圆环

双圆环效果展示了如何使用CSS和JavaScript的巧妙组合实现优雅的动态动画。

HTML结构

<section class="circle__wrapper"><ul class="circle__text__wrapper__left"><li class="circle__text__left__item">design</li><!-- 更多列表项 --></ul><ul class="circle__text__wrapper__right"><li class="circle__text__right__item">design</li><!-- 更多列表项 --></ul>
</section>

JavaScript实现

interface CircleConfig {wrapper: HTMLElement;items: NodeListOf<HTMLElement>;radius: number;direction: number;
}export class Circle {leftConfig: CircleConfig;rightConfig: CircleConfig;centerX!: number;centerY!: number;constructor() {this.leftConfig = {wrapper: document.querySelector(".circle__text__wrapper__left") as HTMLElement,items: document.querySelectorAll(".circle__text__left__item"),radius: 0,direction: 1,};this.rightConfig = {wrapper: document.querySelector(".circle__text__wrapper__right") as HTMLElement,items: document.querySelectorAll(".circle__text__right__item"),radius: 0,direction: -1,};this.updateDimensions();this.init();}updateDimensions(): void {this.centerX = window.innerWidth / 2;this.centerY = window.innerHeight / 2;this.leftConfig.radius = this.leftConfig.wrapper.offsetWidth / 2;this.rightConfig.radius = this.rightConfig.wrapper.offsetWidth / 2;}updateItemsPosition(config: CircleConfig, scrollY: number): void {const { items, radius, direction } = config;const totalItems = items.length;const spacing = Math.PI / totalItems;items.forEach((item, index) => {const angle = index * spacing - scrollY * direction * Math.PI * 2;const x = this.centerX + Math.cos(angle) * radius;const y = this.centerY + Math.sin(angle) * radius;const rotationOffset = direction === -1 ? 180 : 0;const rotation = (angle * 180) / Math.PI + rotationOffset;gsap.set(item, {x,y,rotation,transformOrigin: "center center",});});}createScrollAnimations(): void {ScrollTrigger.create({trigger: ".circle__wrapper",start: "top bottom",end: "bottom top",scrub: 1,onUpdate: (self) => {const scrollY = self.progress * 0.5;this.updateItemsPosition(this.leftConfig, scrollY);this.updateItemsPosition(this.rightConfig, scrollY);},});}
}

第三种效果:隧道

第三种效果引入了"隧道"动画,文本项沿深度轴堆叠,在用户向前滚动场景时产生3D运动感。

HTML结构

<section class="tube__wrapper"><ul class="tube__text__wrapper"><li class="tube__text__item">design</li><!-- 更多列表项 --></ul>
</section>

JavaScript实现

export class Tube {private items: NodeListOf<HTMLElement>;private textWrapper: HTMLElement;private wrapper: HTMLElement;constructor() {this.wrapper = document.querySelector(".tube__wrapper") as HTMLElement;this.textWrapper = document.querySelector(".tube__text__wrapper") as HTMLElement;this.items = document.querySelectorAll(".tube__text__item");this.init();}private calculatePositions(): void {const offset = 0.4;const radius = Math.min(window.innerWidth, window.innerHeight) * offset;const spacing = 360 / this.items.length;this.items.forEach((item, index) => {const angle = (index * spacing * Math.PI) / 180;const x = Math.sin(angle) * radius;const y = 0;const z = Math.cos(angle) * radius;const rotationY = index * spacing;item.style.transform = `translate3d(${x}px, ${y}px, ${z}px) rotateY(${rotationY}deg)`;});}private createScrollTrigger(): void {ScrollTrigger.create({trigger: ".tube__title",start: "center center",end: "+=2000svh",pin: this.wrapper,scrub: 2,animation: gsap.fromTo(this.textWrapper,{ rotateY: 0 },{ rotateY: 360, ease: "none" }),});}
}

结论

感谢您跟随本教程!我们探索了三种独特的3D文本滚动效果——圆柱体、圆环和隧道动画,每种都展示了使用GSAP、ScrollTrigger和创意3D CSS变换构建沉浸式滚动驱动体验的不同方法。

通过调整排版和颜色,您可以为这些效果创建各种迷人的外观!
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

公众号二维码

公众号二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/959232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apifox接口测试工具简介 - 努力-

Apifox接口测试工具简介Apifox简介。一、Apifox介绍 介绍:Apifox是一款集成了Api文档、Api调试、Api Mock、Api测试的一体化协作平台。 作用:接口文档管理、接口请求测试、Mock服务。 官网: https://apifox.com/ 二…

Windows 10 家庭版启用组策略编辑器

Windows 10 家庭版启用组策略编辑器 问题描述 在Windows 10/11家庭版中,运行gpedit.msc(组策略编辑器)时,系统提示“Windows 找不到文件 gpedit.msc”,无法打开组策略编辑器。 原因 Windows家庭版系统默认未预装组…

MySQL 基础架构(一):SQL语句的执行之旅

MySQL 采用经典的分层架构设计,整体可分为 Server 层和存储引擎层两大部分。这种设计实现了核心功能与存储实现的分离,为不同类型的应用场景提供了灵活的存储方案。MySQL系列文章 你是否好奇过,一条看似简单的SQL查…

顺序表练习题

​1. 设顺序表中的元素递增有序,编写一个算法,将元素x插入顺序表L中的适当位置,以保持该顺序表的有序性。 void sortInsert(List *l,int x){//假设表是有序的if(l->length==MAXSIZE)exit(1);//表已满,无法插入f…

关于一种计算递归次数题的思路

代码如下 要求计算最后输出的count的结果 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int count = 0; int fib(int a) {count++;if (a == 0)return 1;else if (a == 1)return 2;elsereturn fib(a - 1) …

前端框架深度解析:Vue 从入门到实战,掌握渐进式开发核心 - 实践

前端框架深度解析:Vue 从入门到实战,掌握渐进式开发核心 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

CF2162G

定义一棵树大小为 \(n\) 的树的权值是:\(S = \sum\limits_{(u, v) \in E} (u \cdot v)\),给定 \(n\),构造一棵权值为完全平方数的树。 \(n \le 2 \times 10^5\)尝试让 \(u\) 固定,那就是菊花图,此时 \(S = u(\fra…

题解:lo6878 生不逢时

写这篇题解的时候回酒店电脑崩了直接没了,也算是照应标题生不逢时了…… 牛牛题。 题意:给定正整数 n, m 和 n 个区间,第 i 个区间为 \([l _ i, r _ i]\),保证 \(0 \leq l_i \leq r_i < 2^m\)。 对于非负整数 \…

【UEGamePlay】- 3C篇(三) : 角色 (二)

前言 上篇文章我们大体梳理了角色相关的移动/旋转框架逻辑,并且初步筛选了我们

stapter WP笔记

很喜欢的靶机,有效暴露了自己的不足,里面的兔子洞基本上全部踩了一遍,所以写一下这篇wp兼笔记 感想:打靶很多时候是反直觉的,有的时候不能基于感觉,或者说觉得概率很小就不去尝试,它是一个严谨的,纯粹理性的过…

【51单片机】【protues仿真】基于51单片机全自动洗衣机系统 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

定金单专题

一、系统中的定金单,支付方式和账户是一一对应的。 二、定金单的支付方式,再Payment method grouping里面是以Card形式出现的,否则定金单的支付找不到对应的支付方式。 三、在系统中下定金单。

练习上传

这是一级标题 MPE教程 这是二级标题 这是三级标题 这会是 斜体 的文字 这会是 斜体 的文字 这会是 粗体 的文字 这会是 粗体 的文字 你也 组合 这些符号 这个文字将会被横线删除 无序列表Item 1 Item 2Item 2a Item 2b…

uniapp修改原生导航栏样式、加图标、加文字、加点击事件 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

CITP——更适合约束接口的CRTP变式 - CLimber

在C++中,前人已经提出了多种约束接口的方式。其中,CRTP不妨是一个适用于低标准且不需要性能的静态多态设计方式。但CRTP无法约束构造函数,实现过程涉及静态转换,代码复杂。为此,我们提出了CRTP的变式——**CITP**…

函数的可变参数传参

一.前言 可变参数传参是C语言的一种高级用法。二. 用法示例 求任意个 int 类型数据的平均值。点击查看代码 double avg_int(int count, ...) {va_list ap; /* 2. 声明参数列表变量 */int sum = 0;va…

P12366 [蓝桥杯 2022 省 Python B] 数位排序

将数字与数位和捆绑(使用结构体或元组)放入数组或列表,以数位和为关键字排序,最后输出第 m 个数。 #include<stdio.h> #include<algorithm> using namespace std; const int MAXN=1e6+7; struct Node{…

重组蛋白表达技术|HEK293细胞蛋白表达|高效重组蛋白生产服务

一、表达系统的技术特性与选择策略 哺乳动物细胞表达系统以其卓越的翻译后修饰能力成为复杂蛋白表达的首选方案。其中,HEK293细胞凭借高转染效率和快速生长特性,在瞬时表达中表现优异;而CHO细胞则因其在悬浮培养中的…

CJI8运行查询没有数据

CJI8运行查询没有数据,发现不是权限问题,是因为查询界面设置的问题。如下: To view the entire transfer in CJI8, the report must be executed for Overall values without the year. ☆ No matter how much you …

Para 集训

Para 给我推的高质量题目,终于是找时间整理出来了。Para 好闪,拜谢 Para!Para 最好啦! [清华集训 2014] 主旋律 abs,第一题就忘了怎么做了。 DAG 计数模板题。 这里有一个经典的 trick:不是强连通分量的总会存在…