Frontend Lab
← 回到所有效果
T043D004three

Wave Plane

波浪平面

難度 ★★★★★標籤 vertex · plane · wireframe3 min read

效果簡述

平面用 vertex 位移做波浪,搭配 wireframe 很有「資料 / AI」的感覺。 原生 Three.js 直接改 BufferGeometry 的 position attribute;更高效是寫 vertex shader,但這版用 JS 操作好閱讀。

預設|振幅 0.3

Claude Code Prompt

💬 Claude Code Prompt
參考:3D004 wave plane
技術:r3f + 每幀改 PlaneGeometry position z

具體行為:
- planeGeometry args={[5, 5, 48, 48]}(段數越多越平滑)
- meshStandardMaterial wireframe 視覺感
- useFrame: 遍歷 pos.count,z = sin(x + t) + cos(y + t)
- pos.needsUpdate = true

限制:
- 段數 32 ~ 64;超過 96 移動裝置卡
- 不要每幀 new Float32Array(用同一個 buffer setZ)
- shader 版效能更好,但需要 GLSL

完整程式碼

WavePlane.tsx
'use client'

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

function Wave() {
const ref = useRef<THREE.Mesh>(null)
useFrame((state) => {
  const m = ref.current; if (!m) return
  const geom = m.geometry as THREE.PlaneGeometry
  const pos = geom.attributes.position as THREE.BufferAttribute
  const t = state.clock.getElapsedTime()
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i); const y = pos.getY(i)
    pos.setZ(i, Math.sin(x * 1.5 + t) * 0.18 + Math.cos(y * 1.5 + t * 0.8) * 0.18)
  }
  pos.needsUpdate = true
})
return (
  <mesh ref={ref} rotation={[-Math.PI/2.4, 0, 0]}>
    <planeGeometry args={[5,5,48,48]} />
    <meshStandardMaterial color="#fb923c" wireframe />
  </mesh>
)
}

export function WavePlane() {
return (
  <Canvas camera={{ position: [0,2,4], fov: 55 }} dpr={[1,2]}>
    <ambientLight intensity={0.6} />
    <directionalLight position={[3,5,3]} intensity={1} />
    <Wave />
  </Canvas>
)
}

何時用

  • ✅ 「資料 / AI」概念站
  • ✅ Loading 視覺
  • ✅ 抽象 Hero 背景

何時別用

  • ❌ 內容區
  • ❌ 一頁多個 wave
  • ❌ 段數高 + 行動裝置

我踩過的坑

  1. 每幀 new Float32Array:吃 GC,FPS 抖。直接 setZ 同一塊 buffer。

  2. needsUpdate = true 忘記寫:改了但不更新 GPU,看起來沒動。

  3. 段數太低看起來像紙箱:48 起跳才算「波」。

相關效果

搜尋

按 ⌘K 隨時開啟