这个背景用的是three.js实现的,其实就是three官网的Example稍微改了一下,比较简单。
react项目中需要安装threejs和tweenjs依赖。
React:
import React from 'react';
import webGlControl from './control';
interface ViewProps extends React.HTMLAttributes<HTMLSpanElement> {
}
interface States {
showing: boolean;
}
export default class PointsBg extends React.Component<ViewProps, States> {
public state:States = {
showing: false,
}
public componentDidMount() {
webGlControl.init('three-comp-points-bg');
setTimeout(() => {
this.startPlay() //开始,外部调用用ref
}, 500)
}
public componentWillUnmount() {
webGlControl.destroy();
}
public render() {
const { children, ...restProps } = this.props;
const { showing } = this.state
return (
<div id="three-comp-points-bg"
style={{ width: '100%', height: '800px', opacity: showing? 1: 0 }}
/>
);
}
public startPlay = () => {
this.setState({showing: true});
webGlControl.restartAnimate()
webGlControl.toggleTween(0)
}
public stopPlay = () => {
webGlControl.toggleTween(-2000, () => {
webGlControl.stopAnimate();
this.setState({showing: false});
});
}
}
control.ts:
import * as Three from 'three';
import TWEEN from '@tweenjs/tween.js';
class WebGlControl {
private container: any;
private camera: any;
private scene: any;
private renderer: any;
private animateFrame: any;
private starsGroup: any; //
private Geometrys: {
starsGeometry?: any;
} = {}; //保存所有Geometry,方便统一销毁
private Materials: {
starsMaterial?: any;
} = {}; //保存所有Material,方便统一销毁
private guiData: any = {
x: 0.005,
color: '#f0f2f5',
autoRotate: true,
};
private mouseData: any = {
mouseX: 0,
mouseY: 0,
};
public init = (id:string) => {
// if(this.scene) {return}
this.container = document.getElementById(id);
let width = this.container.clientWidth,
height = this.container.clientHeight;
this.scene = new Three.Scene();
this.camera = new Three.PerspectiveCamera(75, width / height, 1, 2000);
this.camera.position.x = 0;
this.camera.position.y = 0;
this.camera.position.z = 500;
this.camera.lookAt(this.scene.position);
this.renderer = new Three.WebGLRenderer();
// this.renderer.setClearColor( 0xffffff );
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width, height);
this.container.appendChild(this.renderer.domElement);
this.addThings();
this.animate();
this.initEventListen();
};
public destroy = () => {
Object.values(this.Geometrys).forEach((e) => {
e.dispose();
});
Object.values(this.Materials).forEach((e) => {
e.dispose();
});
this.scene.clear();
this.renderer.dispose();
this.renderer.forceContextLoss();
this.camera = null;
this.scene = null;
this.renderer = null;
cancelAnimationFrame(this.animateFrame);
};
private initEventListen = () => {
this.container.addEventListener('pointermove', this.onPointerMove);
window.addEventListener( 'resize', this.onWindowResize );
};
private onPointerMove = (event) => {
if (event.isPrimary === false) return;
this.mouseData.mouseX = event.clientX - this.container.clientWidth;
this.mouseData.mouseY = event.clientY - this.container.clientHeight;
};
private onWindowResize = () => {
// this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize( window.innerWidth, window.innerHeight );
}
private addThings = () => {
this.starsGroup = new Three.Group();
this.starsGroup.position.z = -2000;
this.scene.add(this.starsGroup);
this.Geometrys.starsGeometry = new Three.BufferGeometry();
let starArr: any[] = [];
for (let i = 0; i < 2000; i++) {
const x = Math.random() * 1200 - 600;
const y = Math.random() * 1200 - 600;
const z = Math.random() * 1200 - 600;
starArr.push(x, y, z);
}
this.Geometrys.starsGeometry.setAttribute(
'position',
new Three.Float32BufferAttribute(starArr, 3),
);
/** 创建一个星星的材质 **/
let canvDom = document.createElement("canvas");
canvDom.width = 100;
canvDom.height = 100;
let context:any = canvDom.getContext("2d");
context.fillStyle = "#ffb51c";
context.strokeStyle = "#ffb51c";
context.moveTo(0,50);
context.bezierCurveTo(25,50,50,25,50,0);
context.bezierCurveTo(50,25,75,50,100,50);
context.bezierCurveTo(75,50,50,75,50,100);
context.bezierCurveTo(50,75,25,50,0,50);
context.stroke();
context.fill();
/** 创建一个星星的材质 **/
// 创建材质
let texture = new Three.Texture(canvDom);
texture.needsUpdate = true;
this.Materials.starsMaterial = new Three.PointsMaterial({
color: 0xffb51c,
depthTest: false,
transparent: true,
size: 6,
map: texture
});
const stars = new Three.Points(
this.Geometrys.starsGeometry,
this.Materials.starsMaterial,
);
this.starsGroup.add(stars);
};
public toggleTween = (z:number, callback?: () => void) => {
// cameray: 20, z: 500
var tween = new TWEEN.Tween(this.starsGroup.position)
.to({ x: 0, y: 0, z }, 1000).start().onComplete(() => {
if(callback) {
callback()
}
});
// tween.easing(TWEEN.Easing.Sinusoidal.InOut);
tween.easing(TWEEN.Easing.Circular.Out);
};
public restartAnimate = () => {
this.stopState = false;
this.animate()
}
public stopAnimate = () => {
this.stopState = true
}
private stopState:boolean = false
private animate = () => {
if(this.stopState) {
return;
}
// if(!this.renderer){return;}
this.animateFrame = requestAnimationFrame(this.animate);
this.render();
TWEEN.update();
};
private render = () => {
if(!this.renderer) {return;}
this.renderer.setClearColor(this.guiData.color, 1.0);
if (this.guiData.autoRotate) {
this.starsGroup.rotation.y += 0.002;
}
this.camera.position.x += ( this.mouseData.mouseX - this.camera.position.x ) * 0.025;
this.camera.position.y += ( - this.mouseData.mouseY - this.camera.position.y ) * 0.025;
this.camera.lookAt(this.scene.position);
this.renderer.render(this.scene, this.camera);
};
}
const webGlControl = new WebGlControl();
export default webGlControl;
现在上面的组件调用后,是自动执行的,如果要自主控制的话,就注释掉componentDidMount里的setTimeout,然后用ref方式调用startPlay,stopPlay方法。