用 Three.js + React 打造一个赛博朋克风格的 3D 作品集页面

用 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

相关推荐
Upsy-Daisy3 小时前
IOTA 学习笔记(一):IOTA 是什么?从区块链到 Tangle
笔记·学习·区块链
软件工程小施同学4 小时前
最新区块链论文录用资讯 CCF A--WWW 2026 12篇
区块链
master-dragon4 小时前
Solidity 智能合约开发实战:从零构建 PiggyBank 存钱罐合约
区块链
酿情师4 小时前
孤立交易:比特币节点为什么会暂存缺少父交易的交易
区块链
拼尽全力前进15 小时前
加密算法分类
区块链
酿情师21 小时前
比特币系统中是如何解决货币的发行与验证两大问题
区块链·比特币
酿情师21 小时前
SPV:比特币轻客户端如何在不下载完整区块链的情况下验证交易
区块链
liudanzhengxi21 小时前
ImToken智能合约交互避坑全攻略
区块链
拼尽全力前进1 天前
比特币和以太坊的底层正是使用了 ECC(椭圆曲线加密)算法来生成钱包地址(公钥衍生)和签署交易(私钥签名) 解释说明
区块链