前言
最近在自学three,看见一个动态曲线的动图,就想着用three实现出来。
最终效果
主要技术栈
react
@react-three/fiber
@react-three/fiber 是一个 React renderer for Three.js,它允许你使用 React 的语法和组件化特性来构建 3D 场景。它是与 Three.js 交互的基础层,提供了以下功能:
- React 集成:使 Three.js 的场景和对象可以像 React 组件那样使用。
- 性能优化:利用 React 的虚拟 DOM 和变更检测机制来提高渲染效率。
- Hooks 支持:支持 React Hooks,如 useFrame 和 useLoader。
- 自动卸载:当组件不再被使用时,自动清理 Three.js 对象以避免内存泄漏。
@react-three/drei
@react-three/drei 是建立在 @react-three/fiber 之上的一个实用库,提供了许多常用的组件和工具,旨在简化常见的 3D 开发任务。这些工具通常包括但不限于:
- 实用组件:如 OrbitControls, Suspense, Grid, Spotlight 等。
- 加载器:用于加载纹理、模型和其他资源。
- 辅助工具:如 useGLTF, useTexture 等。
代码
小玩意,我就直接贴代码,看下注释应该能懂了
'use client';
import React, { useState, useCallback, useContext } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { Line } from '@react-three/drei';
import { AudioContext } from '../lib/contexts/AudioContext';
// 用于绘制动态曲线的组件
function DynamicCurve() {
const [curvePoints, setCurvePoints] = useState([]);
// 在每个帧更新曲线点的位置
// 更新曲线点的位置
useFrame(({ clock }) => {
const time = clock.getElapsedTime();
const newCurvePoints = [];
// 根据时间计算曲线上各点的位置
for (let i = 0; i < 40; i++) {
const x = i / 10 - 2;
const y = Math.sin(3 * time + i / 10);
const z = 0;
newCurvePoints.push([x, y, z]);
}
// 更新曲线的点
setCurvePoints(newCurvePoints);
});
if (curvePoints.length === 0) {
return null; // 如果 curvePoints 为空,则返回 null,以防止渲染线条
}
return (
<Line
points={curvePoints}
color="#fff"
lineWidth={2}
// 曲线的其他属性和样式
/>
);
}
// 暂停状态下显示的静态直线组件
const StaticLine = () => (
<Line
points={[
[-2, 0, 0],
[2, 0, 0],
]}
color="#fff"
lineWidth={2}
/>
);
/**
* 背景音乐按钮组件,用于控制背景音乐的播放和暂停,并显示当前播放状态。
*/
const BackgroundMusicButton = () => {
// 这里我是因为做了个loader加载组件,网站加载完成之后可以选择有声进入还是无声进入
const { isPlaying, play, pause } = useContext(AudioContext);
// 使用useCallback优化播放/暂停函数的性能
// 使用useCallback缓存函数
const togglePlay = useCallback(() => {
if (isPlaying) {
pause();
} else {
play();
}
}, [isPlaying]);
return (
<div className="bg-button">
<button onClick={togglePlay}>
<Canvas frameloop="demand">
{isPlaying ? <DynamicCurve /> : <StaticLine />}
</Canvas>
</button>
</div>
);
};
export default BackgroundMusicButton;