依赖
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;