当前位置: 首页 > news >正文

[3D-portfolio] 3D画布组件 | <Canvas> | Framer Motion | 预定义动画序列

无论是3D效果,还是动画效果,都是调用第三方库来实现的。当做软件需求当中想要实现某一个效果,就可以开始查询需要用到的库,然后根据库的文档来进行学习,就可以尝试调用使用了。

对于库的调用和安排就需要涉及到一些基本的设计思想,像下面的动画渲染实现自动更新的功能,就是用到了分为两部分的解耦设计,动画定义(motion.js)与应用(variants属性)分离


第三章:3D画布组件

欢迎回来!

在上一章《React组件》中,我们学习了标准React组件如何构建网站用户界面的2D部分——包括文本、按钮、导航栏和列表等元素。

但那些令人惊叹的3D元素呢?

悬浮的计算机、旋转的地球仪、科技小球?仅靠标准React组件无法创建这些交互式三维视觉效果。

为此我们需要一种特殊组件:3D画布组件


3D画布组件解决了什么问题?

想象构建传统网站就像布置静态展览——你在墙上排列平面图片和文字。

当要在展览中添加动态三维雕塑时,仅仅有墙面是不够的;你需要专用空间,可能需要搭建舞台或平台来展示、打光雕塑,甚至允许互动

在网页上创建3D图形也是类似的。标准HTML和React组件专为在平面页面上排列2D元素设计。

要渲染复杂3D模型和场景,我们需要网页中的专用"舞台"或"画布"。

这就是3D画布组件的用途。它们是通过强大库来创建和管理这些3D"舞台"的专用组件,为传统网页元素增添动态视觉层级


核心工具:React Three FiberDrei

在React应用中处理3D图形需要以下两个主要工具:

  1. React Three Fiber (@react-three/fiberr3f):这是用React构建3D场景的关键桥梁。我们可以使用熟悉的React组件语法(<mesh>, <light>, <camera>等)描述3D世界,无需直接编写复杂3D代码。就像用React语言编写3D舞台的脚本。
    • 例如后面我们用到的 <Canvas>:来自@react-three/fiber,创建HTML <canvas>并在其中初始化Three.js场景,作为3D对象的"舞台"
  2. Drei (@react-three/drei):这个辅助工具集构建在React Three Fiber之上,提供3D场景常用组件如相机控制(<OrbitControls>)、模型加载器(useGLTF)、实用形状等,通过预制工具节省开发时间。

3D画布组件工作原理

站点的每个3D视觉元素(计算机、地球仪、科技小球、星空)都由专用3D画布组件渲染

这些组件通常位于src/components/canvas目录。

ComputersCanvas.jsx为例(简化版)解析核心结构:

// src/components/canvas/Computers.jsx (简化版)
import React, { Suspense, useEffect, useState } from "react";
import { Canvas } from "@react-three/fiber"; // 导入Canvas组件!
import { OrbitControls, Preload, useGLTF } from "@react-three/drei";
import CanvasLoader from "../Loader"; // 加载指示器// 实际3D模型和场景设置
const Computers = ({ isMobile }) => {const computer = useGLTF("./desktop_pc/scene.gltf"); // 加载模型文件return (<mesh> {/* 3D对象容器 */}<hemisphereLight intensity={0.15} groundColor="black" /> {/* 环境光 */}<pointLight intensity={1} /> {/* 点光源 */}<primitive // 加载的3D模型object={computer.scene}scale={isMobile ? 0.65 : 0.75}position={isMobile ? [0, -3, -2.2] : [0, -3.25, -1.5]}rotation={[-0.01, -0.2, -0.1]}/></mesh>);
};// 3D舞台设置组件
const ComputersCanvas = () => {const [isMobile, setIsMobile] = useState(false);useEffect(() => { /* 设备检测逻辑 */ }, []);return (<Canvas // React Three Fiber提供的3D舞台frameloop="demand" // 按需渲染shadows // 启用阴影camera={{ position: [20, 3, 5], fov: 25 }} // 相机参数gl={{ preserveDrawingBuffer: true }} // WebGL设置><Suspense fallback={<CanvasLoader />}> {/* 加载过渡 */}<OrbitControls // 轨道控制器enableZoom={false}maxPolarAngle={Math.PI / 2}minPolarAngle={Math.PI / 2}/><Computers isMobile={isMobile} /> {/* 3D内容 */}</Suspense><Preload all /> {/* 资源预加载 */}</Canvas>);
};export default ComputersCanvas;

关键组件解析:

  1. <Canvas>:来自@react-three/fiber创建HTML <canvas>并在其中初始化Three.js场景,作为3D对象的"舞台"
  2. SuspenseCanvasLoader:处理3D模型加载时的过渡状态
  3. OrbitControls:来自Drei相机轨道控制器
  4. Computers组件:定义场景内容,使用useGLTF加载模型文件

3D画布组件应用实例

案例1:主视觉计算机

src/components/Hero.jsx中简单引入:

import { ComputersCanvas } from "./canvas";
//...
<section className="relative w-full h-screen mx-auto"><ComputersCanvas /> {/* 3D组件插入 */}
</section>

案例2:联系页地球仪

src/components/Contact.jsx布局:

import { EarthCanvas } from "./canvas";
//...
<motion.div className="xl:flex-1 xl:h-auto md:h-[550px] h-[350px]"><EarthCanvas /> {/* 地球组件 */}
</motion.div>

案例3:技术栈小球

src/components/Tech.jsx动态生成:

import { BallCanvas } from "./canvas";
//...
{technologies.map((tech) => (<div key={tech.name}><BallCanvas icon={tech.icon} /> {/* 带图标的小球 */}</div>
))}

技术实现流程

在这里插入图片描述

常见的第三方库转化逻辑:初始化 定义控制器 转化 渲染

核心3D组件清单

组件文件路径渲染内容使用位置特性
ComputersCanvascanvas/Computers.jsx悬浮计算机Hero.jsx响应式布局,轨道控制
EarthCanvascanvas/Earth.jsx旋转地球仪Contact.jsx自动旋转,轨道控制
BallCanvascanvas/Ball.jsx带图标小球Tech.jsx使用Drei贴图技术
StarsCanvascanvas/Stars.jsx星空背景App.jsx基础几何体与粒子系统

所有组件通过src/components/canvas/index.js统一导出。

结语

3D-Portfolio项目中3D视觉效果的实现奥秘。

3D画布组件通过**React Three FiberDrei**创建专用3D舞台封装了相机、光源、模型与控制器的复杂配置

让我们能用React组件化思维整合动态3D图形

下一章我们将探索如何通过动画工具(Framer Motion)为2D元素添加流畅动效


第四章:动画工具(Framer Motion)

在上一章《3D画布组件》中,我们深入探讨了项目如何通过React Three Fiber等专用组件和库创建酷炫的3D视觉效果。

现在让我们将注意力转回网站的传统2D部分——滚动时看到的文本、图像和版块。

为了让作品集呈现更生动的交互体验,这些元素不会突然出现,而是通过平滑的入场动画展现。

这正是**动画工具(Framer Motion)**的用武之地。

动画工具解决的问题

想象观看一场没有过渡效果的幻灯片演示,突兀的切换会削弱观众体验。

同理,当网页元素随着滚动突然出现时,也会显得呆板生硬。

动画通过添加平滑过渡视觉修饰能:

  • 引导用户关注重点内容
  • 增强网站的现代感和动态感
  • 通过视觉反馈提升用户体验

本项目通过**预定义动画序列**替代重复编写复杂动画代码,这些序列如同为网页元素编排好的"舞蹈动作"。

动画编排指南:src/utils/motion.js与Framer Motion

本项目的"动画编排师"是Framer Motion——一个流行的React动画库

而"编舞指南"则是位于src/utils/motion.js的文件。

该文件包含通过Framer Motion概念定义常见动画模式的JavaScript函数,这些模式称为 “变体(variants)”。

核心概念:motion组件与variants

Framer Motion通过为HTML元素添加motion.前缀(如<motion.div><motion.p><motion.img>)将其转换为动态组件

这些motion组件具备动画能力,通过variants属性控制动画效果:

  • motion组件:标准元素的动画版本,如同舞台上的"舞者"
  • variants定义动画"状态"的JavaScript对象,通常包含hidden(动画前状态)和show(动画后状态)。
    • (Everything is state machine)
    • src/utils/motion.js中的工具函数帮助我们轻松创建这些对象

动画工具使用示例

src/components/About.jsx的"About"版块为例:

首先导入所需模块:

// src/components/About.jsx(代码片段)
import { motion } from "framer-motion"; // 导入Framer Motion
import { fadeIn, textVariant } from "../utils/motion"; // 导入动画工具
// ... 其他导入

为需要动画的元素包裹motion.组件并添加variants属性:

// src/components/About.jsx(代码片段 - About组件内部)
return (<>{/* 标题/副标题容器动画 */}<motion.div variants={textVariant()}> {/* 使用motion.div */}<p className={styles.sectionSubText}>简介</p><h2 className={styles.sectionHeadText}>概述.</h2></motion.div>{/* "关于我"段落动画 */}<motion.pvariants={fadeIn("", "", 0.1, 1)} // 应用fadeIn变体className="mt-4 text-secondary text-[17px] max-w-3xl leading-[30px]">{personalInfo.about} {/* 来自constants的内容(第一章) */}</motion.p>{/* ... ServiceCard元素(如下所示)... */}</>
);

在此示例中:

  1. 包含"简介"和"概述"的div改用<motion.div>
  2. 通过variants属性应用从src/utils/motion.js导入的textVariant()工具函数
  3. "关于我"段落改用<motion.p>并应用fadeIn变体

这些variants对象定义动画状态,而动画触发时机通常由initial/animatewhileInView/viewport等属性控制。

在本项目中,动画常在元素滚动至视口时触发,这由SectionWrapper(下章详述)自动处理,原理是在主容器设置initial="hidden"whileInView="show",Framer Motion会将"show"状态传播给子motion组件。

观察About.jsx中的ServiceCard组件:

// src/components/About.jsx(代码片段 - ServiceCard组件)
const ServiceCard = ({ index, title, icon }) => {return (<Tilt /* ... Tilt参数 */>{/* 卡片容器动画 */}<motion.divvariants={fadeIn("right", "spring", index * 0.5, 0.75)} // 应用渐变变体className="w-full green-pink-gradient p-[1px] rounded-[20px] shadow-card">{/* ... 卡片内容(图标、标题) */}</motion.div></Tilt>);
};

此处每个ServiceCard的主div都是motion.div

variants应用带index * 0.5延迟的fadeIn变体,创建交错动画效果,使卡片依次渐入呈现波浪效果。

其他组件的应用模式类似:

  • Contact.jsx:通过motion.div配合slideIn("left")slideIn("right")实现表单与地球模型的左右滑动入场

    // src/components/Contact.jsx(代码片段)
    import { motion } from "framer-motion";
    import { slideIn } from "../utils/motion";
    // ...<motion.divvariants={slideIn("left", "tween", 0.2, 1)} // 左侧滑入className="relative flex-[0.75] bg-black-100 p-8 rounded-2xl"
    >{/* ... 表单内容 ... */}
    </motion.div><motion.divvariants={slideIn("right", "tween", 0.2, 1)} // 右侧滑入className="xl:flex-1 xl:h-auto md:h-[550px] h-[350px]"
    ><EarthCanvas /> {/* 3D地球组件 */}
    </motion.div>
    
  • Works.jsxProjectCard组件使用带index延时的fadeIn("up")实现项目卡片弹性上浮

    // src/components/Works.jsx(代码片段 - ProjectCard组件)
    import { motion } from "framer-motion";
    import { fadeIn } from "../utils/motion";
    // ...const ProjectCard = ({ index, /* ... 属性 */ }) => {return (<motion.div variants={fadeIn("up", "spring", index * 0.5, 0.75)}> {/* 弹性上浮 */}<Tilt /* ... Tilt参数 */>{/* ... 项目卡片内容 ... */}</Tilt></motion.div>);
    };
    

底层原理:src/utils/motion.js解析

src/utils/motion.js包含返回Framer Motion可识别variants对象的JavaScript函数。

查看简化版的fadeIn函数:

// src/utils/motion.js(简化代码片段)export const fadeIn = (direction, type, delay, duration) => {return {hidden: { // 初始状态(动画前)x: direction === "left" ? 100 : direction === "right" ? -100 : 0, // X轴偏移y: direction === "up" ? 100 : direction === "down" ? -100 : 0,   // Y轴偏移opacity: 0, // 完全透明},show: { // 结束状态(动画后)x: 0, // 回归原始水平位置y: 0, // 回归原始垂直位置opacity: 1, // 完全可见transition: { // 定义动画过渡type: type,       // 动画类型(如"spring"弹性、"tween"补间)delay: delay,     // 延迟时间duration: duration, // 持续时间ease: "easeOut",  // 缓动函数},},};
};// ... 其他变体函数(textVariant、slideIn、zoomIn、staggerContainer)

fadeIn函数接收参数并返回包含hiddenshow属性的对象:

  • hidden:定义初始状态(如透明度0,位置偏移)
  • show:定义结束状态(如完全可见,位置归零)
  • transition:配置动画类型、延迟、持续时间和缓动函数

<motion.div>应用variants={fadeIn(...)}时,Framer Motion读取该对象

若父组件或视口触发"show"状态,库会自动将CSS属性从hidden值渐变至show值。

其他函数如textVariantslideIn等以类似方式运作,实现不同动画效果。

整体运作流程

在这里插入图片描述

  1. 组件渲染:React组件(如About.jsx)渲染包含motion组件的JSX
  2. 变体定义motion组件通过variants属性调用工具函数获取动画定义
  3. 库接收配置:Framer Motion接收变体对象及initial/animate等属性
  4. 初始状态应用:将hidden状态样式应用到DOM元素
  5. 动画触发:根据触发条件(如进入视口)启动过渡
  6. 样式渐变:库计算中间状态并应用渐变
  7. 浏览器更新:动态更新显示效果

此架构将动画定义(motion.js)与应用(variants属性)分离,提升复用性与可维护性。

总结

本章探讨了**动画工具(Framer Motion)**的应用。

我们了解到src/utils/motion.js通过Framer Motion库提供预设动画模式,这些"变体"通过motion组件的variants属性应用,为作品集的2D元素添加渐入、滑动、弹性等动态效果。

工具函数返回的hidden/show状态与transition配置,使Framer Motion能自动完成元素状态的平滑过渡。

接下来我们将探讨如何通过版块包装高阶组件(Section Wrapper HOC)实现样式统一与动画自动触发。

版块包装高阶组件


如果想要实现一个实时的效果,就可以借助这个架构:
动画定义(motion.js)与应用(variants属性)分离,提升复用性与可维护性。

相关文章:

  • 网站建设方案书模板 备案seo站内优化和站外优化
  • 淘宝上开做网站的店铺十大室内设计网站
  • 常熟公司网站建设电话品牌型网站制作价格
  • 自建站英文抖音引流推广怎么做
  • 如何找广告商合作郑州外语网站建站优化
  • 正规网站模板设计crm系统成功案例分享ppt
  • OpenCV边缘填充方式详解
  • 设置cursor、vscode的默认终端
  • VIVADO设定寄存器/存储器的初始值
  • Java+LangChain实战入门:深度剖析开发大语言模型应用!
  • [论文阅读] 人工智能+ | 用大语言模型给建筑合规检查“开挂“:BIM领域的自动化革命
  • PHP的
  • DeepSeek16-open-webui Pipelines开发填坑
  • 课堂笔记:吴恩达的AI课(AI FOR EVERYONE)-W1 机器学习什么能做,什么不能做
  • 算法 按位运算
  • 缓存与加速技术实践-MongoDB数据库应用
  • 阿里云ACP-检索分析服务
  • 深入解析Python多服务器监控告警系统:从原理到生产部署
  • 解锁阿里云Datatransport:数据迁移的终极利器
  • 向量数据库milvus中文全文检索取不到数据的处理办法
  • ISP Pipeline(5): Auto White Balance Gain Control (AWB) 自动白平衡
  • 城市综合管廊监测,智能化安全监测,多源感知,三维可视化监控
  • ASIO 避坑指南:高效、安全与稳健的异步网络编程
  • 基于SpringBoot的智慧旅游系统
  • 六个安全Agent设计模式:有效防止Prompt注入攻击
  • Serverless新宠:阿里云SAE,解锁应用部署新姿势