Frontend Lab
← 回到所有效果
T023D002three

Particle Field

粒子場

難度 ★★★★★標籤 particles · points · three3 min read

效果簡述

大量小點分布在 3D 空間,緩慢旋轉。Hero 背景、Loading 過場很合適。 用 points + pointsMaterial + bufferAttribute,比每個粒子做 mesh 快百倍。

預設|1500 顆星

Claude Code Prompt

💬 Claude Code Prompt
參考:3D002 particle field
技術:r3f points + bufferGeometry

具體行為:
- useMemo 生成 Float32Array (count*3) 的位置
- <points><bufferGeometry><bufferAttribute attach="attributes-position" args={[positions, 3]}/></bufferGeometry><pointsMaterial size={0.035} sizeAttenuation/></points>
- useFrame 慢慢旋轉整個 group

限制:
- count 1000 ~ 3000 最佳;超過 5000 移動裝置卡
- pointsMaterial color 用單色(差異化用 size 就好)
- 不要每幀重算 positions(吃 CPU)

完整程式碼

ParticleField.tsx
'use client'

import { Canvas, useFrame } from '@react-three/fiber'
import { useMemo, useRef } from 'react'
import * as THREE from 'three'

function Particles({ count = 1500 }) {
const ref = useRef<THREE.Points>(null)
const positions = useMemo(() => {
  const arr = new Float32Array(count * 3)
  for (let i = 0; i < count; i++) {
    arr[i*3] = (Math.random() - 0.5) * 8
    arr[i*3+1] = (Math.random() - 0.5) * 8
    arr[i*3+2] = (Math.random() - 0.5) * 8
  }
  return arr
}, [count])
useFrame((_, d) => { if (ref.current) ref.current.rotation.y += d * 0.06 })
return (
  <points ref={ref}>
    <bufferGeometry>
      <bufferAttribute attach="attributes-position" args={[positions, 3]} count={count} />
    </bufferGeometry>
    <pointsMaterial size={0.035} color="#fb923c" sizeAttenuation />
  </points>
)
}

export function ParticleField() {
return (
  <Canvas camera={{ position: [0,0,5], fov: 55 }} dpr={[1,2]}>
    <Particles />
  </Canvas>
)
}

何時用

  • ✅ Hero 背景(科技感)
  • ✅ Loading 過場
  • ✅ AI / 資料相關產品的視覺隱喻

何時別用

  • ❌ 內容區(背景動會分心)
  • ❌ 一頁多個粒子場
  • ❌ 行動裝置電池敏感場景

我踩過的坑

  1. 沒 useMemo 每次 render 重算:count 大時整頁 lag。

  2. sizeAttenuation false 看起來很假:開了才有「遠的小、近的大」 的透視感。

  3. bufferAttribute 第三個參數 itemSize 是 3:xyz。寫成 1 會出怪。

相關效果

搜尋

按 ⌘K 隨時開啟