307 lines
9.1 KiB
JavaScript
307 lines
9.1 KiB
JavaScript
// Galaxy background - moonlit terrain with floating stars
|
|
// Includes FPS monitoring and fallback to simple background
|
|
(function initGalaxy(){
|
|
console.clear();
|
|
|
|
// FPS monitoring
|
|
let lastTime = performance.now();
|
|
let frameCount = 0;
|
|
let fps = 60;
|
|
let lowFpsCount = 0;
|
|
const FPS_THRESHOLD = 27;
|
|
const LOW_FPS_SAMPLES = 5; // Switch after 5 consecutive low FPS readings
|
|
let isSimpleMode = false;
|
|
|
|
var ww = window.innerWidth,
|
|
wh = window.innerHeight;
|
|
|
|
var renderer = new THREE.WebGLRenderer({
|
|
antialias: true,
|
|
canvas: document.querySelector('#galaxy-canvas')
|
|
});
|
|
renderer.setSize(ww, wh);
|
|
renderer.setClearColor(0x001a2d);
|
|
|
|
var scene = new THREE.Scene();
|
|
scene.fog = new THREE.Fog(0x001a2d, 80, 140);
|
|
|
|
var camera = new THREE.PerspectiveCamera(45, ww/wh, 0.1, 200);
|
|
camera.position.x = 70;
|
|
camera.position.y = 30;
|
|
camera.position.z = 5;
|
|
camera.lookAt(new THREE.Vector3());
|
|
|
|
/* LIGHTS */
|
|
var moonLight = new THREE.PointLight(0xffffff, 2, 150);
|
|
scene.add(moonLight);
|
|
|
|
var moon;
|
|
function createMoon() {
|
|
var geometry = new THREE.SphereGeometry(8, 32, 32);
|
|
var material = new THREE.MeshPhongMaterial({
|
|
color: 0x26fdd9,
|
|
shininess: 15,
|
|
emissive: 0x2bb2e6,
|
|
emissiveIntensity: 0.8
|
|
});
|
|
moon = new THREE.Mesh(geometry, material);
|
|
moon.position.x = -9;
|
|
moon.position.z = -6.5;
|
|
moon.position.y = 1;
|
|
moon.rotation.y = -1;
|
|
scene.add(moon);
|
|
moonLight.position.copy(moon.position);
|
|
moonLight.position.y += 4;
|
|
var moonLight2 = new THREE.PointLight(0xffffff, 0.6, 150);
|
|
scene.add(moonLight2);
|
|
moonLight2.position.x += 20;
|
|
moonLight2.position.y -= 20;
|
|
moonLight2.position.z -= 25;
|
|
}
|
|
|
|
// Initialize simplex noise
|
|
var noise = new SimplexNoise();
|
|
|
|
function createTerrain() {
|
|
var geometry = new THREE.PlaneGeometry(150, 150, 120, 120);
|
|
var m = new THREE.Matrix4();
|
|
m.makeRotationX(Math.PI * -0.5);
|
|
geometry.applyMatrix4(m);
|
|
|
|
var positions = geometry.attributes.position;
|
|
for(var i = 0; i < positions.count; i++) {
|
|
var x = positions.getX(i);
|
|
var z = positions.getZ(i);
|
|
var ratio = noise.noise3D(x * 0.03, z * 0.03, 0);
|
|
positions.setY(i, ratio * 10);
|
|
}
|
|
positions.needsUpdate = true;
|
|
geometry.computeVertexNormals();
|
|
|
|
var material = new THREE.MeshPhongMaterial({
|
|
color: 0x198257,
|
|
emissive: 0x032f50
|
|
});
|
|
var plane = new THREE.Mesh(geometry, material);
|
|
scene.add(plane);
|
|
}
|
|
|
|
var stars = new THREE.Group();
|
|
scene.add(stars);
|
|
var starsLights = new THREE.Group();
|
|
scene.add(starsLights);
|
|
var starsAmount = 20;
|
|
|
|
function createStars() {
|
|
var geometry = new THREE.SphereGeometry(0.3, 16, 16);
|
|
var material = new THREE.MeshBasicMaterial({color: 0xffffff});
|
|
for(var i = 0; i < starsAmount; i++) {
|
|
var star = new THREE.Mesh(geometry, material);
|
|
star.position.x = (Math.random() - 0.5) * 150;
|
|
star.position.z = (Math.random() - 0.5) * 150;
|
|
var ratio = noise.noise3D(star.position.x * 0.03, star.position.z * 0.03, 0);
|
|
star.position.y = ratio * 10 + 0.3;
|
|
stars.add(star);
|
|
var velX = (Math.random() + 0.1) * 0.1 * (Math.random() < 0.5 ? -1 : 1);
|
|
var velY = (Math.random() + 0.1) * 0.1 * (Math.random() < 0.5 ? -1 : 1);
|
|
star.vel = new THREE.Vector2(velX, velY);
|
|
var starLight = new THREE.PointLight(0xffffff, 0.8, 3);
|
|
starLight.position.copy(star.position);
|
|
starLight.position.y += 0.5;
|
|
starsLights.add(starLight);
|
|
}
|
|
}
|
|
|
|
function updateStar(star, index) {
|
|
if(star.position.x < -75) {
|
|
star.position.x = 75;
|
|
}
|
|
if(star.position.x > 75) {
|
|
star.position.x = -75;
|
|
}
|
|
if(star.position.z < -75) {
|
|
star.position.z = 75;
|
|
}
|
|
if(star.position.z > 75) {
|
|
star.position.z = -75;
|
|
}
|
|
|
|
star.position.x += star.vel.x;
|
|
star.position.z += star.vel.y;
|
|
var ratio = noise.noise3D(star.position.x * 0.03, star.position.z * 0.03, 0);
|
|
star.position.y = ratio * 10 + 0.3;
|
|
|
|
starsLights.children[index].position.copy(star.position);
|
|
starsLights.children[index].position.y += 0.5;
|
|
}
|
|
|
|
function render(a) {
|
|
requestAnimationFrame(render);
|
|
|
|
if (!isSimpleMode) {
|
|
// FPS monitoring (only when not in simple mode)
|
|
frameCount++;
|
|
const currentTime = performance.now();
|
|
const delta = currentTime - lastTime;
|
|
|
|
if (delta >= 500) { // Update FPS every 500ms
|
|
fps = Math.round((frameCount * 1000) / delta);
|
|
console.log(`FPS: ${fps} (threshold: ${FPS_THRESHOLD}, low samples: ${lowFpsCount}/${LOW_FPS_SAMPLES})`);
|
|
frameCount = 0;
|
|
lastTime = currentTime;
|
|
|
|
// Check if FPS is consistently low
|
|
if (fps < FPS_THRESHOLD) {
|
|
lowFpsCount++;
|
|
console.warn(`⚠️ Low FPS detected! ${fps} FPS (${lowFpsCount}/${LOW_FPS_SAMPLES})`);
|
|
if (lowFpsCount >= LOW_FPS_SAMPLES) {
|
|
console.warn(`🔄 Switching to simple background mode...`);
|
|
switchToSimpleBackground();
|
|
isSimpleMode = true;
|
|
// Stop monitoring - mode persists until page refresh
|
|
}
|
|
} else {
|
|
lowFpsCount = 0; // Reset counter if FPS improves
|
|
}
|
|
}
|
|
|
|
// Render 3D scene
|
|
for(var i = 0; i < starsAmount; i++) {
|
|
updateStar(stars.children[i], i);
|
|
}
|
|
renderer.render(scene, camera);
|
|
}
|
|
// If in simple mode, just keep the animation frame loop running but do nothing
|
|
}
|
|
|
|
function switchToSimpleBackground() {
|
|
console.log('🎨 Activating simple background mode...');
|
|
|
|
// Stop rendering 3D scene
|
|
const canvas = document.querySelector('#galaxy-canvas');
|
|
canvas.style.display = 'none';
|
|
console.log('✓ 3D canvas hidden');
|
|
|
|
// Create simple gradient background
|
|
const overlay = document.querySelector('#background-overlay');
|
|
overlay.style.background = 'linear-gradient(180deg, #0a1628 0%, #001a2d 50%, #000d1a 100%)';
|
|
overlay.style.backdropFilter = 'none';
|
|
console.log('✓ Gradient background applied');
|
|
|
|
// Disable all animations and transitions
|
|
disableAnimations();
|
|
console.log('✓ Animations disabled');
|
|
|
|
// Add simple animated stars
|
|
createSimpleStars();
|
|
console.log('✓ Simple stars created');
|
|
|
|
console.log('✅ Simple background mode activated - animations disabled for performance');
|
|
}
|
|
|
|
function disableAnimations() {
|
|
// Create a style tag to disable animations
|
|
const style = document.createElement('style');
|
|
style.id = 'low-fps-mode';
|
|
style.textContent = `
|
|
/* Disable all animations and transitions in low FPS mode */
|
|
*, *::before, *::after {
|
|
animation-duration: 0s !important;
|
|
animation-delay: 0s !important;
|
|
transition-duration: 0s !important;
|
|
transition-delay: 0s !important;
|
|
}
|
|
|
|
/* Remove hover effects and transforms */
|
|
.service-card:hover,
|
|
.card:hover {
|
|
transform: none !important;
|
|
box-shadow: 0 4px 12px rgba(79,70,229,0.3) !important;
|
|
}
|
|
|
|
.control-btn:hover,
|
|
.theme-toggle:hover,
|
|
#ddg-search-btn:hover {
|
|
transform: none !important;
|
|
}
|
|
|
|
/* Disable status dot animations */
|
|
.status-dot {
|
|
animation: none !important;
|
|
}
|
|
|
|
/* Disable shimmer effects */
|
|
.service-card::before,
|
|
.card::before {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Simplify search input focus */
|
|
#search-input:focus {
|
|
box-shadow: 0 0 0 2px rgba(79,70,229,0.3) !important;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
function createSimpleStars() {
|
|
const starsContainer = document.createElement('div');
|
|
starsContainer.id = 'simple-stars';
|
|
starsContainer.style.cssText = `
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
`;
|
|
|
|
// Create 50 simple star divs
|
|
for (let i = 0; i < 50; i++) {
|
|
const star = document.createElement('div');
|
|
star.style.cssText = `
|
|
position: absolute;
|
|
width: 2px;
|
|
height: 2px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
top: ${Math.random() * 100}%;
|
|
left: ${Math.random() * 100}%;
|
|
opacity: ${Math.random() * 0.5 + 0.3};
|
|
animation: twinkle ${Math.random() * 3 + 2}s infinite ease-in-out;
|
|
`;
|
|
starsContainer.appendChild(star);
|
|
}
|
|
|
|
// Add twinkle animation
|
|
if (!document.querySelector('#simple-stars-style')) {
|
|
const style = document.createElement('style');
|
|
style.id = 'simple-stars-style';
|
|
style.textContent = `
|
|
@keyframes twinkle {
|
|
0%, 100% { opacity: 0.3; }
|
|
50% { opacity: 1; }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
|
|
document.body.appendChild(starsContainer);
|
|
}
|
|
|
|
function onResize() {
|
|
ww = window.innerWidth;
|
|
wh = window.innerHeight;
|
|
camera.aspect = ww / wh;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(ww, wh);
|
|
}
|
|
|
|
createMoon();
|
|
createTerrain();
|
|
createStars();
|
|
requestAnimationFrame(render);
|
|
window.addEventListener('resize', onResize);
|
|
})();
|