依赖
three(这个重要) react (这个不重要) ahooks(这个不重要) unocss(这个不重要)
效果
代码
import React, { useEffect, useRef } from 'react' ;
import { useGetState } from 'ahooks' ;
import * as THREE from 'three' ;
const Index = ( ) => {
const threeRef = useRef ( null ) ;
const [ , setCamera, getCamera] = useGetState ( null ) ;
const [ , setRenderer, getRenderer] = useGetState ( null ) ;
const [ , setScene, getScene] = useGetState ( null ) ;
const [ , setParticles, getParticles] = useGetState ( null ) ;
const [ , setCount, getCount] = useGetState ( 0 ) ;
const SEPARATION = 100 ,
AMOUNT_X = 200 ,
AMOUNT_Y = 200 ;
const init = ( ) => {
const newCamera = new THREE. PerspectiveCamera ( 75 , window. innerWidth / window. innerHeight, 1 , 10000 ) ;
newCamera. position. z = 1000 ;
const newScene = new THREE. Scene ( ) ;
setScene ( newScene) ;
const numParticles = AMOUNT_X * AMOUNT_Y ;
const positions = new Float32Array ( numParticles * 3 ) ;
const scales = new Float32Array ( numParticles) ;
let i = 0 ,
j = 0 ;
for ( let ix = 0 ; ix < AMOUNT_X ; ix++ ) {
for ( let iy = 0 ; iy < AMOUNT_Y ; iy++ ) {
positions[ i] = ix * SEPARATION - ( AMOUNT_X * SEPARATION ) / 2 ;
positions[ i + 1 ] = 0 ;
positions[ i + 2 ] = iy * SEPARATION - ( AMOUNT_Y * SEPARATION ) / 1 ;
scales[ j] = 1 ;
i += 3 ;
j++ ;
}
}
const geometry = new THREE. BufferGeometry ( ) ;
geometry. setAttribute ( 'position' , new THREE. BufferAttribute ( positions, 3 ) ) ;
geometry. setAttribute ( 'scale' , new THREE. BufferAttribute ( scales, 1 ) ) ;
const material = new THREE. ShaderMaterial ( {
uniforms : {
color : { value : new THREE. Color ( 0x6380fb ) } ,
} ,
vertexShader : `
attribute float scale;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = scale * ( 300.0 / - mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
` ,
fragmentShader : `
uniform vec3 color;
void main() {
if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
gl_FragColor = vec4( color, 1.0 );
}
` ,
} ) ;
const newParticles = new THREE. Points ( geometry, material) ;
newScene. add ( newParticles) ;
setParticles ( newParticles) ;
const newRenderer = new THREE. WebGLRenderer ( { antialias : true } ) ;
newRenderer. setPixelRatio ( window. devicePixelRatio) ;
newRenderer. setSize ( window. innerWidth, window. innerHeight) ;
newRenderer. setClearColor ( 0x0b1121 , 1.0 ) ;
threeRef. current. appendChild ( newRenderer. domElement) ;
setRenderer ( newRenderer) ;
window. addEventListener ( 'resize' , onWindowResize) ;
setCamera ( newCamera) ;
animate ( ) ;
} ;
const onWindowResize = ( ) => {
const newCamera = getCamera ( ) ;
const newRenderer = getRenderer ( ) ;
if ( ! newCamera) return ;
newCamera. aspect = window. innerWidth / window. innerHeight;
newCamera. updateProjectionMatrix ( ) ;
newRenderer?. setSize ( window. innerWidth, window. innerHeight) ;
} ;
const animate = ( ) => {
requestAnimationFrame ( animate) ;
render ( ) ;
} ;
const render = ( ) => {
const newCamera = getCamera ( ) ;
const newScene = getScene ( ) ;
const newParticles = getParticles ( ) ;
const newRenderer = getRenderer ( ) ;
const count = getCount ( ) ;
if ( ! newCamera) return ;
newCamera. position. x += ( 0 - newCamera. position. x) * 1 ;
newCamera. position. y += ( 400 - newCamera. position. y) * 1 ;
newCamera. lookAt ( newScene. position) ;
const positions = newParticles. geometry. attributes. position. array;
const scales = newParticles. geometry. attributes. scale. array;
let i = 0 ,
j = 0 ;
for ( let ix = 0 ; ix < AMOUNT_X ; ix++ ) {
for ( let iy = 0 ; iy < AMOUNT_Y ; iy++ ) {
positions[ i + 1 ] = Math. sin ( ( ix + count) * 0.3 ) * 50 + Math. sin ( ( iy + count) * 0.5 ) * 50 ;
scales[ j] = ( Math. sin ( ( ix + count) * 0.3 ) + 1 ) * 20 + ( Math. sin ( ( iy + count) * 0.5 ) + 1 ) * 20 ;
i += 3 ;
j++ ;
}
}
newParticles. geometry. attributes. position. needsUpdate = true ;
newParticles. geometry. attributes. scale. needsUpdate = true ;
newRenderer. render ( newScene, newCamera) ;
setCount ( ( val ) => ( val += 0.1 ) ) ;
} ;
useEffect ( ( ) => {
if ( threeRef. current && ! getCamera ( ) ) {
init ( ) ;
}
} , [ threeRef] ) ;
return < div className= "h-full w-full" ref= { threeRef} > < / div> ;
} ;
export default Index;