对个人主页设计和实现感兴趣的朋友可以订阅我的专栏哦!!谢谢大家!!!
在这篇博客中,我们将分析一个极其有趣和互动性的组件 - Experience.jsx
,该组件用于在主页中呈现个人的工作经历。
这个组件最大的趣味就是集成了一个 3D 动作人物(Developer Model),当您悬停或点击经历时,人物会切换动作,极大地增强了互动环节和设计感!
React + ThreeJS 个人主页小项目:🎥 07 · 职业经历区实现解析 Experience 组件✨(互动动作 + 3D 角色 + 点击切换动画)
✨ 项目主要技术点
- 引入
Developer
人物组件进行 3D 动作演示; OrbitControls
控制滑鼠旋转视角;useState
管理动作名,通过点击 / 悬停切换动作;- 右侧是职历列表,左侧是动作人物显示区域;
📁 项目结构建议
组件路径:
bash
src/
├--- components/
│ └--- Developer.jsx
├--- sections/
│ └--- Experience.jsx
└--- constants/
├--- index.js
🌟 主体应用组件解析
▶右侧: 职历列表分析
职历列表来自 workExperiences
定义数据源,通过 .map()
进行动态渲染:
js
{workExperiences.map((item, index) => (
<div
key={index}
onClick={() => setAnimationName(item.animation.toLowerCase())}
onPointerOver={() => setAnimationName(item.animation.toLowerCase())}
onPointerOut={() => setAnimationName('idle')}
className="work-content_container group"
>
...
</div>
))}
三种互动方式以下:
onClick
点击切换指定动作onPointerOver
悬停时切换onPointerOut
离开时返回 idle 动作
▶左侧: 人物动作 Canvas
html
<Canvas>
<ambientLight intensity={5} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<directionalLight position={[10, 10, 10]} intensity={1} />
<OrbitControls enableZoom={false} maxPolarAngle={Math.PI / 2} />
<Suspense fallback={<CanvasLoader />}>
<Developer position={[0, -3, 0.5]} scale={3} animationName={animationName} />
</Suspense>
</Canvas>
Developer
是一个包含多种动作的 3D 模型组件- 根据
animationName
作为Props传入Developer
组件中,用于同步切换动作 CanvasLoader
在模型加载期间显示处理
▶ 切换动作原理 Developer.jsx
- 动画资源加载
javascript
const { animations: idleAnimation } = useFBX('/models/animations/idle.fbx');
const { animations: saluteAnimation } = useFBX('/models/animations/salute.fbx');
const { animations: clappingAnimation } = useFBX('/models/animations/clapping.fbx');
const { animations: victoryAnimation } = useFBX('/models/animations/victory.fbx');
组件通过useFBX钩子加载了四种不同的动画文件,分别对应空闲、敬礼、鼓掌和胜利姿势。
- 动画命名和注册
javascript
idleAnimation[0].name = 'idle';
saluteAnimation[0].name = 'salute';
clappingAnimation[0].name = 'clapping';
victoryAnimation[0].name = 'victory';
const { actions } = useAnimations(
[idleAnimation[0], saluteAnimation[0], clappingAnimation[0], victoryAnimation[0]],
group,
);
每个动画被赋予一个唯一的名称,然后通过useAnimations钩子注册到动画系统中。
- 动画切换控制
javascript
useEffect(() => {
console.log(actions[animationName])
actions[animationName].reset().fadeIn(0.5).play();
return () => {
if (actions[animationName]) {
actions[animationName].fadeOut(0.5);
}
};
}, [animationName, actions]);
当animationName属性发生变化时,组件会:
- 重置当前动画状态
- 使用0.5秒的渐入效果播放新动画
- 在清理函数中使用0.5秒的渐出效果结束当前动画
- 工作流程
- 父组件通过animationName属性控制要播放的动画
- 当属性变化时,useEffect监听到变化并触发动画切换
- 新动画平滑渐入,旧动画平滑渐出,实现流畅的动画过渡
📂 workExperiences 数据结构格式
js
{
id: 1,
name: 'Framer',
pos: 'Lead Web Developer',
duration: '2022 - Present',
title: "Framer serves as my go-to tool for creating interactive prototypes. I use it to bring designs to life, allowing stakeholders to experience the user flow and interactions before development.",
icon: '/assets/framer.svg',
animation: 'clapping',
}
animation
字段是关键,应与 Developer 组件支持的动作名一致;- 这些数据可以装载于公共 constants 文件中,便于扩展维护
- 假设数据中有一条职历动作名为
Thinking
,前端会根据操作把animationName
切换为thinking
,传给Developer
,实现互动切换。

👉 小结
- 该组件是整个主页最具创意的一段
- 使用简单易懂的 state + props 切换动作
- 集成了完善的左侧渲染 + 右侧 Canvas 互动
🔠 下一篇预告:Contact区域
我们将在下一节中:
🌟 构建联系我们的表单组件(Contact)
✨ 使用emailjs
完成表单提交