vue使用three.js实现仿黑神话悟空跑动-脚下波纹特效
<template><div ref="container" class="container"></div>
</template><script>
import * as THREE from "three";
export default {name: "WaterRipple",data() {return {scene: null,camera: null,renderer: null,player: null,clock: new THREE.Clock(),keyboard: {},rippleMaterial: null,ripples: []};},mounted() {this.init();this.animate();document.addEventListener("keydown", this.onDocumentKeyDown, false);document.addEventListener("keyup", this.onDocumentKeyUp, false);},beforeDestroy() {document.removeEventListener("keydown", this.onDocumentKeyDown, false);document.removeEventListener("keyup", this.onDocumentKeyUp, false);},methods: {init() {// 创建场景this.scene = new THREE.Scene();// 创建相机this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);this.camera.position.set(0, 50, 100);this.camera.lookAt(0, 0, 0);// 创建渲染器this.renderer = new THREE.WebGLRenderer();this.renderer.setSize(window.innerWidth, window.innerHeight);this.$refs.container.appendChild(this.renderer.domElement);// 创建平面const geometry = new THREE.PlaneGeometry(200, 200, 32, 32);const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });const plane = new THREE.Mesh(geometry, material);plane.rotation.x = -Math.PI / 2;this.scene.add(plane);// 创建玩家const playerGeometry = new THREE.SphereGeometry(1, 32, 32);const playerMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });this.player = new THREE.Mesh(playerGeometry, playerMaterial);this.player.position.y = 1;this.scene.add(this.player);// 窗口调整window.addEventListener('resize', this.onWindowResize, false);},onWindowResize() {this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();this.renderer.setSize(window.innerWidth, window.innerHeight);},createRipple(x, z) {const rippleGeometry = new THREE.RingGeometry(0.1, 0.5, 32);const rippleMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 1, wireframe: true });const ripple = new THREE.Mesh(rippleGeometry, rippleMaterial);ripple.position.set(x, 0.1, z);ripple.rotation.x = -Math.PI / 2;ripple.scale.set(1, 1, 1);this.scene.add(ripple);this.ripples.push({ mesh: ripple, startTime: this.clock.getElapsedTime() });},onDocumentKeyDown(event) {this.keyboard[event.key] = true;},onDocumentKeyUp(event) {this.keyboard[event.key] = false;},animate() {requestAnimationFrame(this.animate);const delta = this.clock.getDelta();const elapsedTime = this.clock.getElapsedTime();const step = 10 * delta;if (this.keyboard['w']) {this.player.position.z -= step;this.createRipple(this.player.position.x, this.player.position.z);}if (this.keyboard['a']) {this.player.position.x -= step;this.createRipple(this.player.position.x, this.player.position.z);}if (this.keyboard['s']) {this.player.position.z += step;this.createRipple(this.player.position.x, this.player.position.z);}if (this.keyboard['d']) {this.player.position.x += step;this.createRipple(this.player.position.x, this.player.position.z);}// 更新水波纹this.ripples.forEach(ripple => {const age = elapsedTime - ripple.startTime;ripple.mesh.scale.set(1 + age * 10, 1 + age * 10, 1 + age * 10);ripple.mesh.material.opacity = Math.max(0, 1 - age / 0.5); // 水波纹消散更快ripple.mesh.material.color.setHSL(0.6, 1, 0.5 * (1 - age / 0.5)); // 颜色随着扩散变淡});this.ripples = this.ripples.filter(ripple => ripple.mesh.material.opacity > 0);this.renderer.render(this.scene, this.camera);}}
};
</script><style scoped>
.container {width: 100vw;height: 100vh;overflow: hidden;
}
</style>