用 Three.js + React 打造一个赛博朋克风格的 3D 作品集页面
嗨,我是欧阳瑞。今天给大家分享一个我最近做的个人项目------用 Three.js 和 React 构建一个充满赛博朋克感的 3D 作品集页面。
为什么做这个?
作为极客玩家和赛博朋克文化的忠实粉丝,我一直想做一个能展示我技术实力的作品集网站。普通的 2D 页面太平淡了,3D 才能体现极客的浪漫。
技术选型
- Next.js 14 - App Router 架构,体验丝滑
- React Three Fiber - Three.js 的 React 封装,写 3D 就像写组件
- Tailwind CSS - 快速构建赛博朋克风格 UI
- @react-three/drei - R3F 的工具库,提供很多实用组件
项目初始化
bash
npx create-next-app@latest cyber-portfolio --typescript --tailwind --app
cd cyber-portfolio
npm install three @types/three @react-three/fiber @react-three/drei
构建 3D 场景
首先创建一个动态的网格地面,这能给整个场景增添科技感:
tsx
// components/GridFloor.tsx
"use client";
import { useRef } from "react";
import { Mesh } from "three";
import { useFrame } from "react-three/fiber";
export function GridFloor() {
const meshRef = useRef<Mesh>(null);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.z = state.clock.elapsedTime * 0.05;
}
});
return (
<mesh ref={meshRef} rotation={[-Math.PI / 2, 0, 0]} position={[0, -1, 0]}>
<planeGeometry args={[50, 50, 50, 50]} />
<meshBasicMaterial wireframe color="#00ffff" transparent opacity={0.3} />
</mesh>
);
}
悬浮的 3D 项目卡片
每个项目展示为一个悬浮的立方体,点击后展开详情:
tsx
// components/ProjectCard.tsx
"use client";
import { useRef, useState } from "react";
import { Mesh } from "three";
import { useFrame } from "react-three/fiber";
import { Html } from "@react-three/drei";
interface ProjectCardProps {
title: string;
description: string;
position: [number, number, number];
}
export function ProjectCard({ title, description, position }: ProjectCardProps) {
const meshRef = useRef<Mesh>(null);
const [hovered, setHovered] = useState(false);
const [clicked, setClicked] = useState(false);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.x = state.clock.elapsedTime * 0.3;
meshRef.current.rotation.y = state.clock.elapsedTime * 0.2;
const targetY = clicked ? 1 : hovered ? 0.5 : 0;
meshRef.current.position.y = position[1] +
THREE.MathUtils.lerp(meshRef.current.position.y - position[1], targetY, 0.1);
}
});
return (
<mesh
ref={meshRef}
position={position}
onClick={() => setClicked(!clicked)}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<boxGeometry args={[1.5, 1.5, 1.5]} />
<meshStandardMaterial
color={hovered ? "#ff00ff" : "#00ffff"}
metalness={0.8}
roughness={0.2}
emissive={hovered ? "#ff00ff" : "#00ffff"}
emissiveIntensity={hovered ? 0.5 : 0.2}
/>
{clicked && (
<Html position={[0, 0, 1.5]} center>
<div className="bg-black/90 border border-cyan-400 p-4 rounded w-64 text-cyan-400">
<h3 className="text-lg font-bold mb-2">{title}</h3>
<p className="text-sm">{description}</p>
</div>
</Html>
)}
</mesh>
);
}
霓虹文字效果
赛博朋克怎么能少了霓虹灯效果?用 Text 组件可以实现:
tsx
// components/NeonTitle.tsx
"use client";
import { Text } from "@react-three/drei";
import { useRef } from "react";
import { useFrame } from "react-three/fiber";
export function NeonTitle() {
const textRef = useRef<any>(null);
useFrame((state) => {
if (textRef.current) {
textRef.current.material.emissiveIntensity =
0.5 + Math.sin(state.clock.elapsedTime * 2) * 0.3;
}
});
return (
<Text
ref={textRef}
position={[-3, 3, 0]}
fontSize={0.8}
color="#ff00ff"
anchorX="left"
font="/fonts/cyberpunk.woff"
>
RICH.OWN
</Text>
);
}
相机控制器
让用户可以自由探索场景:
tsx
// app/page.tsx
"use client";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, PerspectiveCamera } from "@react-three/drei";
import { GridFloor } from "@/components/GridFloor";
import { ProjectCard } from "@/components/ProjectCard";
import { NeonTitle } from "@/components/NeonTitle";
export default function Home() {
const projects = [
{ title: "DeFi Swap", description: "去中心化交易所", position: [-2, 1, 0] as [number, number, number] },
{ title: "NFT Gallery", description: "3D 画廊应用", position: [2, 1, 0] as [number, number, number] },
{ title: "DAO Tool", description: "治理工具", position: [0, 1, -2] as [number, number, number] },
];
return (
<div className="w-full h-screen bg-black">
<Canvas>
<PerspectiveCamera makeDefault position={[0, 2, 8]} />
<OrbitControls enableDamping dampingFactor={0.05} />
<ambientLight intensity={0.2} />
<pointLight position={[10, 10, 10]} intensity={1} color="#00ffff" />
<pointLight position={[-10, -10, -10]} intensity={1} color="#ff00ff" />
<NeonTitle />
<GridFloor />
{projects.map((project, index) => (
<ProjectCard
key={index}
title={project.title}
description={project.description}
position={project.position}
/>
))}
</Canvas>
<div className="absolute bottom-8 left-8 text-cyan-400">
<p className="text-sm">Drag to explore · Click cards for details</p>
</div>
</div>
);
}
添加样式
确保 Tailwind 配置支持赛博朋克风格:
js
// tailwind.config.js
module.exports = {
content: ["./app/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
cyber: {
cyan: "#00ffff",
magenta: "#ff00ff",
yellow: "#ffff00",
},
},
fontFamily: {
cyber: ["var(--font-cyber)"],
},
},
},
plugins: [],
};
运行看看效果
bash
npm run dev
打开 http://localhost:3000,你应该能看到一个炫酷的 3D 赛博朋克风格作品集页面。鼠标拖拽可以旋转视角,点击悬浮的立方体可以查看项目详情。
总结
Three.js 配合 React Three Fiber 让 3D 开发变得前所未有的简单。如果你也想做一个与众不同的个人网站,不妨试试这套组合。我的鬃狮蜥 Hash 都觉得这个效果很酷(虽然它表面上很淡定)。
有问题欢迎留言交流,我是欧阳瑞,极客之路,永无止境!
技术栈:React · Next.js · Three.js · React Three Fiber · Tailwind CSS