如何使用React-Three-Fiber创建动态图像动画
通过旋转纹理、3D几何体和React Three Fiber驱动的流畅运动,让静态视觉效果焕发生机。
视图与相机设置
相机的视野(FOV)在这个项目中起着关键作用。我们将其设置为较低值以模拟正交相机的效果,但使用透视相机可以方便后续尝试不同的FOV值。
<PerspectiveCamera makeDefault fov={7} position={[0, 0, 70]} near={0.01} far={100000} />
设置3D形状
创建基本组件
Billboard.tsx - 显示图像堆栈的圆柱体组件:
'use client';
import { useRef } from 'react';
import * as THREE from 'three';
function Billboard({ radius = 5, ...props }) {
const ref = useRef(null);
return (
<mesh ref={ref} {...props}>
<cylinderGeometry args={[radius, radius, 2, 100, 1, true]} />
<meshBasicMaterial color="red" side={THREE.DoubleSide} />
</mesh>
);
}
Banner.tsx - 类似移动横幅的圆柱体组件:
'use client';
import * as THREE from 'three';
import { useRef } from 'react';
function Banner({ radius = 1.6, ...props }) {
const ref = useRef(null);
return (
<mesh ref={ref} {...props}>
<cylinderGeometry
args={[radius, radius, radius * 0.07, radius * 80, radius * 10, true]}
/>
<meshBasicMaterial
color="blue"
side={THREE.DoubleSide}
/>
</mesh>
);
}
构建完整形状结构
'use client';
import styles from './page.module.scss';
import Billboard from '@/components/webgl/Billboard/Billboard';
import Banner from '@/components/webgl/Banner/Banner';
import { View } from '@/webgl/View';
import { PerspectiveCamera } from '@react-three/drei';
const COUNT = 10;
const GAP = 3.2;
export default function Home() {
return (
<div className={styles.page}>
<View className={styles.view} orbit={false}>
<PerspectiveCamera makeDefault fov={7} position={[0, 0, 70]} near={0.01} far={100000} />
<group rotation={[-0.15, 0, -0.2]}>
{Array.from({ length: COUNT }).map((_, index) => [
<Billboard
key={`billboard-${index}`}
radius={5}
position={[0, (index - (Math.ceil(COUNT / 2) - 1)) * GAP, 0]}
rotation={[0, index * Math.PI * 0.5, 0]}
/>,
<Banner
key={`banner-${index}`}
radius={5}
rotation={[0, 0, 0.085]}
position={[0, (index - (Math.ceil(COUNT / 2) - 1)) * GAP - GAP * 0.5, 0]}
/>,
])}
</group>
</View>
</div>
);
}
使用Canvas创建图像纹理
纹理生成工具函数
import * as THREE from 'three';
async function preloadImage(imageUrl, axis, canvasHeight, canvasWidth) {
// 图像预加载实现
}
export async function getCanvasTexture({
images,
gap = 10,
canvasHeight = 512,
canvasWidth = 512,
canvas,
ctx,
axis = 'x',
}) {
// 完整纹理生成逻辑
}
React Hook封装
import { useState, useEffect, useCallback } from 'react';
import { getCanvasTexture } from '@/webgl/helpers/getCanvasTexture';
export function useCollageTexture(images, options = {}) {
// Hook实现细节
}
纹理映射与动画
Billboard组件纹理映射
'use client';
import * as THREE from 'three';
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
function setupCylinderTextureMapping(texture, dimensions, radius, height) {
// 纹理映射计算逻辑
}
function Billboard({ texture, dimensions, radius = 5, ...props }) {
const ref = useRef(null);
setupCylinderTextureMapping(texture, dimensions, radius, 2);
useFrame((state, delta) => {
if (texture) texture.offset.x += delta * 0.001;
});
return (
<mesh ref={ref} {...props}>
<cylinderGeometry args={[radius, radius, 2, 100, 1, true]} />
<meshImageMaterial map={texture} side={THREE.DoubleSide} toneMapped={false} />
</mesh>
);
}
自定义材质开发
MeshImageMaterial.js - 背面变暗效果:
import * as THREE from 'three';
import { extend } from '@react-three/fiber';
export class MeshImageMaterial extends THREE.MeshBasicMaterial {
constructor(parameters = {}) {
super(parameters);
this.setValues(parameters);
}
onBeforeCompile = (shader) => {
// 着色器修改逻辑
};
}
MeshBannerMaterial.js - 渐变背面效果:
import * as THREE from 'three';
import { extend } from '@react-three/fiber';
export class MeshBannerMaterial extends THREE.MeshBasicMaterial {
constructor(parameters = {}) {
super(parameters);
// 材质参数设置
}
onBeforeCompile = (shader) => {
// 渐变着色器实现
};
}
最终实现效果
通过组合这些技术组件,我们成功创建了具有以下特性的动态图像动画系统:
- 基于Canvas的动态纹理生成
- 精确的3D几何体纹理映射
- 实时纹理偏移动画
- 自定义着色器材质效果
- 响应式3D场景构建
这种技术方法可以扩展到其他形状和动画效果,为Web端的3D图形展示提供了强大的技术基础。