----优化登录界面
This commit is contained in:
272
src/views/secondDev/StarBackground3.vue
Normal file
272
src/views/secondDev/StarBackground3.vue
Normal file
@ -0,0 +1,272 @@
|
||||
<template>
|
||||
<div class="star-background">
|
||||
<div ref="starContainer" class="star-container"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import * as THREE from 'three';
|
||||
import particleSprite from '../../assets/images/particle-sprite.png';
|
||||
|
||||
const starContainer = ref(null);
|
||||
let scene = null;
|
||||
let camera = null;
|
||||
let renderer = null;
|
||||
let mesh = null;
|
||||
let animationId = null;
|
||||
let texture = null;
|
||||
const fieldRadius = 20;
|
||||
const fieldZLength = 40;
|
||||
|
||||
const initScene = async () => {
|
||||
if (!starContainer.value) return;
|
||||
|
||||
// 创建场景和相机
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
starContainer.value.clientWidth / starContainer.value.clientHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.z = 5;
|
||||
|
||||
// 创建渲染器
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
preserveDrawingBuffer: true // 保留缓冲区以确保正确渲染
|
||||
});
|
||||
renderer.setSize(
|
||||
starContainer.value.clientWidth,
|
||||
starContainer.value.clientHeight
|
||||
);
|
||||
renderer.setPixelRatio(window.devicePixelRatio); // 处理高DPI屏幕
|
||||
starContainer.value.appendChild(renderer.domElement);
|
||||
|
||||
// 创建星空几何体
|
||||
createStars();
|
||||
};
|
||||
|
||||
const createStars = () => {
|
||||
if (!scene) return;
|
||||
|
||||
const corner = [];
|
||||
const colorMix = [];
|
||||
const geoIndex = [];
|
||||
const posArr = [];
|
||||
const uvArr = [];
|
||||
|
||||
const geo = new THREE.BufferGeometry();
|
||||
|
||||
// 循环生成星星
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const [x, y, z] = [
|
||||
THREE.MathUtils.mapLinear(Math.random(), 0, 1, -fieldRadius, fieldRadius),
|
||||
THREE.MathUtils.mapLinear(Math.random(), 0, 1, -fieldRadius, fieldRadius),
|
||||
THREE.MathUtils.mapLinear(Math.random(), 0, 1, -fieldZLength / 2, fieldZLength / 2)
|
||||
];
|
||||
|
||||
const mix = Math.random();
|
||||
for (let j = 0; j < 4; j++) {
|
||||
posArr.push(x, y, z);
|
||||
corner.push(j);
|
||||
colorMix.push(mix);
|
||||
}
|
||||
|
||||
uvArr.push(0, 1);
|
||||
uvArr.push(1, 1);
|
||||
uvArr.push(0, 0);
|
||||
uvArr.push(1, 0);
|
||||
|
||||
const index = 4 * i;
|
||||
geoIndex.push(index, index + 1, index + 2);
|
||||
geoIndex.push(index + 1, index + 3, index + 2);
|
||||
}
|
||||
|
||||
geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(posArr), 3));
|
||||
geo.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvArr), 2));
|
||||
geo.setAttribute('corner', new THREE.BufferAttribute(new Float32Array(corner), 1));
|
||||
geo.setAttribute('colorMix', new THREE.BufferAttribute(new Float32Array(colorMix), 1));
|
||||
geo.setIndex(geoIndex);
|
||||
geo.needsUpdate = true;
|
||||
|
||||
// 顶点着色器
|
||||
const vertexShader = `
|
||||
attribute float corner;
|
||||
attribute float colorMix;
|
||||
uniform float u_time;
|
||||
varying vec2 vUv;
|
||||
varying vec3 vColor;
|
||||
|
||||
uniform float zMin;
|
||||
uniform float zMax;
|
||||
uniform vec3 color1;
|
||||
uniform vec3 color2;
|
||||
|
||||
void main() {
|
||||
vUv = uv;
|
||||
vec3 pos = position;
|
||||
pos.z += u_time;
|
||||
pos.z = mod(pos.z, zMax - zMin) + zMin; // 修复模运算
|
||||
|
||||
|
||||
vec4 worldPos = modelMatrix * vec4(pos, 1.0);
|
||||
vec4 viewPosition = viewMatrix * worldPos;
|
||||
|
||||
float offset = 0.1;
|
||||
|
||||
if (corner == 0.0) {
|
||||
viewPosition.xy += vec2(-offset, -offset);
|
||||
}
|
||||
if (corner == 1.0) {
|
||||
viewPosition.xy += vec2(offset, -offset);
|
||||
}
|
||||
if (corner == 2.0) {
|
||||
viewPosition.xy += vec2(-offset, offset);
|
||||
}
|
||||
if (corner == 3.0) {
|
||||
viewPosition.xy += vec2(offset, offset);
|
||||
}
|
||||
|
||||
vColor = mix(color1, color2, colorMix);
|
||||
|
||||
gl_Position = projectionMatrix * viewPosition;
|
||||
}
|
||||
`;
|
||||
|
||||
// 片元着色器
|
||||
const fragmentShader = `
|
||||
varying vec2 vUv;
|
||||
varying vec3 vColor;
|
||||
uniform sampler2D u_texture;
|
||||
void main() {
|
||||
vec4 texel = texture2D(u_texture, vUv);
|
||||
|
||||
float alpha = texel.r;
|
||||
|
||||
vec3 color = mix(vColor, vec3(1.0), texel.r);
|
||||
|
||||
gl_FragColor = vec4(color, alpha);
|
||||
}
|
||||
`;
|
||||
|
||||
// 加载粒子纹理
|
||||
const loader = new THREE.TextureLoader();
|
||||
const particleTexture = loader.load(particleSprite);
|
||||
|
||||
// 创建材质
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader,
|
||||
uniforms: {
|
||||
u_texture: {
|
||||
value: particleTexture // 使用粒子纹理
|
||||
},
|
||||
color1: {
|
||||
value: new THREE.Color(0x3068FF)
|
||||
},
|
||||
color2: {
|
||||
value: new THREE.Color(0xF34F94)
|
||||
},
|
||||
zMin: {
|
||||
value: -fieldZLength / 2
|
||||
},
|
||||
zMax: {
|
||||
value: fieldZLength / 2 // 修复zMax值
|
||||
},
|
||||
u_time: {
|
||||
value: 0
|
||||
},
|
||||
},
|
||||
transparent: true,
|
||||
blending: THREE.AdditiveBlending // 添加混合模式
|
||||
});
|
||||
|
||||
// 创建网格
|
||||
mesh = new THREE.Mesh(geo, material);
|
||||
scene.add(mesh);
|
||||
};
|
||||
|
||||
const animate = () => {
|
||||
if (!scene || !camera || !renderer) return;
|
||||
|
||||
animationId = requestAnimationFrame(animate);
|
||||
|
||||
if (mesh) {
|
||||
mesh.material.uniforms.u_time.value += 0.02;
|
||||
}
|
||||
|
||||
renderer.render(scene, camera);
|
||||
};
|
||||
|
||||
const onResize = () => {
|
||||
if (!starContainer.value || !camera || !renderer) return;
|
||||
|
||||
camera.aspect = starContainer.value.clientWidth / starContainer.value.clientHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(
|
||||
starContainer.value.clientWidth,
|
||||
starContainer.value.clientHeight
|
||||
);
|
||||
};
|
||||
|
||||
const dispose = () => {
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
|
||||
// 清理几何体
|
||||
if (mesh) {
|
||||
if (mesh.geometry) mesh.geometry.dispose();
|
||||
if (mesh.material) {
|
||||
if (Array.isArray(mesh.material)) {
|
||||
mesh.material.forEach(material => material.dispose());
|
||||
} else {
|
||||
mesh.material.dispose();
|
||||
}
|
||||
}
|
||||
scene.remove(mesh);
|
||||
}
|
||||
|
||||
// 清理渲染器
|
||||
if (renderer) {
|
||||
renderer.dispose();
|
||||
}
|
||||
|
||||
// 清理dom元素
|
||||
if (renderer && renderer.domElement && starContainer.value && starContainer.value.contains(renderer.domElement)) {
|
||||
starContainer.value.removeChild(renderer.domElement);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick(); // 确保DOM已渲染
|
||||
initScene();
|
||||
animate();
|
||||
window.addEventListener('resize', onResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
dispose();
|
||||
window.removeEventListener('resize', onResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.star-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.star-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden; /* 防止出现滚动条 */
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user