File manager - Edit - /home/webapp69.cm.in.th/u69319090046/01_portfolio/app.js
Back
document.addEventListener('DOMContentLoaded', () => { /* ================================================== 0. Light/Dark Theme Toggle & Persistent State ================================================== */ const themeToggleBtn = document.getElementById('theme-toggle'); // Check saved theme or system preference const savedTheme = localStorage.getItem('theme'); const prefersLight = window.matchMedia('(prefers-color-scheme: light)').matches; if (savedTheme === 'light' || (!savedTheme && prefersLight)) { document.body.classList.add('light-theme'); updateToggleIcon(true); } else { updateToggleIcon(false); } if (themeToggleBtn) { themeToggleBtn.addEventListener('click', () => { const isLightNow = document.body.classList.toggle('light-theme'); localStorage.setItem('theme', isLightNow ? 'light' : 'dark'); updateToggleIcon(isLightNow); showToast(isLightNow ? 'เปลี่ยนเป็นโหมดสว่าง (Switched to Light Mode)' : 'เปลี่ยนเป็นโหมดมืด (Switched to Dark Mode)', 'info'); }); } function updateToggleIcon(isLight) { if (!themeToggleBtn) return; const icon = themeToggleBtn.querySelector('i'); if (isLight) { icon.className = 'fa-solid fa-moon'; // Moon icon represents switching to Dark Mode } else { icon.className = 'fa-solid fa-sun'; // Sun icon represents switching to Light Mode } } /* ================================================== 1. Binary Stream Background Effect in Hero ================================================== */ const streamContainer = document.getElementById('binary-stream'); if (streamContainer) { const columns = 8; const rows = 12; let grid = []; // Initialize grid for (let i = 0; i < rows; i++) { let row = []; for (let j = 0; j < columns; j++) { row.push(Math.round(Math.random())); } grid.push(row); } function updateStream() { // Shift rows down and generate new first row grid.pop(); let newRow = []; for (let j = 0; j < columns; j++) { newRow.push(Math.round(Math.random() * 0.7)); // Bias slightly towards 0/space } grid.unshift(newRow); // Render grid streamContainer.textContent = grid.map(row => row.map(val => val === 1 ? '1' : Math.random() > 0.5 ? '0' : ' ').join(' ') ).join('\n'); } setInterval(updateStream, 200); } /* ================================================== 2. Mobile Menu Toggle ================================================== */ const navToggle = document.querySelector('.nav-toggle'); const navMenu = document.querySelector('.nav-menu'); if (navToggle && navMenu) { navToggle.addEventListener('click', () => { navMenu.classList.toggle('open'); const icon = navToggle.querySelector('i'); if (navMenu.classList.contains('open')) { icon.classList.replace('fa-bars', 'fa-xmark'); } else { icon.classList.replace('fa-xmark', 'fa-bars'); } }); // Close menu when clicking on nav links document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', () => { navMenu.classList.remove('open'); navToggle.querySelector('i').classList.replace('fa-xmark', 'fa-bars'); }); }); } /* ================================================== 3. Scrollspy - Highlight Active Link on Scroll ================================================== */ const sections = document.querySelectorAll('section'); const navLinks = document.querySelectorAll('.nav-link'); window.addEventListener('scroll', () => { let currentSectionId = ''; const scrollPosition = window.scrollY + 100; sections.forEach(section => { const sectionTop = section.offsetTop; const sectionHeight = section.clientHeight; if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) { currentSectionId = section.getAttribute('id'); } }); navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${currentSectionId}`) { link.classList.add('active'); } }); }); /* ================================================== 4. Intersection Observer - Scroll Reveal ================================================== */ const revealElements = document.querySelectorAll('.scroll-reveal'); const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const revealObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('active'); observer.unobserve(entry.target); } }); }, observerOptions); revealElements.forEach(el => revealObserver.observe(el)); /* ================================================== 5. Interactive Tuning Lab - Dashboard Logic & Simulation ================================================== */ // Toggles & Controls const toggleRam = document.getElementById('toggle-ram'); const toggleCpu = document.getElementById('toggle-cpu'); const toggleOs = document.getElementById('toggle-os'); const btnReset = document.getElementById('reset-tuning'); // Values to display const valLatency = document.getElementById('val-latency'); const valFps = document.getElementById('val-fps'); const valTemp = document.getElementById('val-temp'); // Trends to display const trendLatency = document.getElementById('trend-latency'); const trendFps = document.getElementById('trend-fps'); const trendTemp = document.getElementById('trend-temp'); // Graph Elements const cardLatency = document.getElementById('card-latency'); const cardFps = document.getElementById('card-fps'); const cardTemp = document.getElementById('card-temp'); const graphStatus = document.getElementById('graph-status'); // Base state configurations (unoptimized) const baseStats = { latency: 18.2, fps: 120, temp: 78 }; // Current state values let currentStats = { ...baseStats }; let displayStats = { ...baseStats }; function calculateTargetStats() { let latency = baseStats.latency; let fps = baseStats.fps; let temp = baseStats.temp; // Apply RAM optimization impact if (toggleRam.checked) { latency -= 4.2; // Lower latency fps += 18; // More FPS temp += 2; // Slight temp increase due to memory controller load } // Apply CPU undervolting impact if (toggleCpu.checked) { latency -= 1.2; // Tiny latency decrease due to stable clock speeds fps += 6; // Slight FPS boost (prevents thermal throttling) temp -= 13; // Significant temp decrease } // Apply OS optimization impact if (toggleOs.checked) { latency -= 6.4; // Large latency decrease fps += 26; // Large FPS boost // No temperature change } return { latency: parseFloat(latency.toFixed(1)), fps: Math.round(fps), temp: Math.round(temp) }; } function updateTelemetryUI() { const targets = calculateTargetStats(); // Setup card visual boundaries depending on active optimizations // Latency if (targets.latency <= 8.0) { trendLatency.textContent = 'Stable Latency'; trendLatency.className = 'telemetry-trend text-green'; cardLatency.style.borderColor = 'rgba(57, 255, 20, 0.3)'; cardLatency.style.boxShadow = '0 0 15px rgba(57, 255, 20, 0.05)'; } else if (targets.latency <= 13.0) { trendLatency.textContent = 'Reduced Latency'; trendLatency.className = 'telemetry-trend text-accent'; cardLatency.style.borderColor = 'rgba(0, 255, 234, 0.3)'; cardLatency.style.boxShadow = 'var(--border-glow)'; } else { trendLatency.textContent = 'High Jitter'; trendLatency.className = 'telemetry-trend text-red'; cardLatency.style.borderColor = 'rgba(255, 51, 102, 0.3)'; cardLatency.style.boxShadow = '0 0 15px rgba(255, 51, 102, 0.05)'; } // FPS if (targets.fps >= 160) { trendFps.textContent = 'Excellent Speed'; trendFps.className = 'telemetry-trend text-green'; cardFps.style.borderColor = 'rgba(57, 255, 20, 0.3)'; } else if (targets.fps >= 140) { trendFps.textContent = 'Improved'; trendFps.className = 'telemetry-trend text-accent'; cardFps.style.borderColor = 'rgba(0, 255, 234, 0.3)'; } else { trendFps.textContent = 'Standard'; trendFps.className = 'telemetry-trend text-yellow'; cardFps.style.borderColor = 'transparent'; } // CPU Temp if (targets.temp <= 66) { trendTemp.textContent = 'Cool & Stable'; trendTemp.className = 'telemetry-trend text-green'; cardTemp.style.borderColor = 'rgba(57, 255, 20, 0.3)'; } else if (targets.temp <= 75) { trendTemp.textContent = 'Normal'; trendTemp.className = 'telemetry-trend text-accent'; cardTemp.style.borderColor = 'rgba(0, 255, 234, 0.3)'; } else { trendTemp.textContent = 'Warm'; trendTemp.className = 'telemetry-trend text-yellow'; cardTemp.style.borderColor = 'transparent'; } // Status graph dot const activeTogglesCount = (toggleRam.checked ? 1 : 0) + (toggleCpu.checked ? 1 : 0) + (toggleOs.checked ? 1 : 0); if (activeTogglesCount === 3) { graphStatus.innerHTML = '<span class="status-dot pulsating-green"></span> Maximum Latency Optimization Achieved'; graphStatus.className = 'graph-status text-green'; } else if (activeTogglesCount > 0) { graphStatus.innerHTML = '<span class="status-dot pulsating-green"></span> Partially Optimized'; graphStatus.className = 'graph-status text-accent'; } else { graphStatus.innerHTML = '<span class="status-dot pulsating-red"></span> Unoptimized State'; graphStatus.className = 'graph-status text-red'; } currentStats = targets; } // Animate numbers smoothly function lerp(start, end, amt) { return (1 - amt) * start + amt * end; } function animateStatsValue() { displayStats.latency = lerp(displayStats.latency, currentStats.latency, 0.1); displayStats.fps = lerp(displayStats.fps, currentStats.fps, 0.1); displayStats.temp = lerp(displayStats.temp, currentStats.temp, 0.1); // Update DOM elements valLatency.textContent = displayStats.latency.toFixed(1); valFps.textContent = Math.round(displayStats.fps); valTemp.textContent = Math.round(displayStats.temp); requestAnimationFrame(animateStatsValue); } // Attach listeners to toggles [toggleRam, toggleCpu, toggleOs].forEach(toggle => { if (toggle) { toggle.addEventListener('change', updateTelemetryUI); } }); if (btnReset) { btnReset.addEventListener('click', () => { toggleRam.checked = false; toggleCpu.checked = false; toggleOs.checked = false; updateTelemetryUI(); showToast('System optimization reset to default.', 'info'); }); } // Initialize telemetry values & start animation updateTelemetryUI(); animateStatsValue(); /* ================================================== 6. Real-time Latency/Frame-Time Canvas Graph ================================================== */ const canvas = document.getElementById('telemetry-graph'); if (canvas) { const ctx = canvas.getContext('2d'); let points = []; const maxPoints = 60; // Initialize points with baseline random latency (16ms to 24ms) for (let i = 0; i < maxPoints; i++) { points.push(18 + (Math.random() - 0.5) * 4); } // Adjust canvas resolution for sharp drawing function resizeCanvas() { const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width * window.devicePixelRatio; canvas.height = 180 * window.devicePixelRatio; ctx.scale(window.devicePixelRatio, window.devicePixelRatio); // Set css dimensions canvas.style.width = '100%'; canvas.style.height = '100%'; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); function drawGraph() { const width = canvas.width / window.devicePixelRatio; const height = canvas.height / window.devicePixelRatio; ctx.clearRect(0, 0, width, height); // 1. Draw horizontal grid lines const isLight = document.body.classList.contains('light-theme'); ctx.strokeStyle = isLight ? 'rgba(0, 0, 0, 0.06)' : 'rgba(255, 255, 255, 0.03)'; ctx.lineWidth = 1; for (let y = 30; y < height; y += 40) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } // 2. Generate new point based on current telemetry state // Calculate current latency dynamically const targetLat = currentStats.latency; // Noise (Jitter) is high in unoptimized states, very low when optimized let noiseLevel = 4.0; if (toggleRam.checked) noiseLevel -= 1.2; if (toggleOs.checked) noiseLevel -= 1.8; if (toggleCpu.checked) noiseLevel -= 0.5; noiseLevel = Math.max(0.2, noiseLevel); // Telemetry peaks (micro-stutters) occur randomly in unoptimized states let stutter = 0; if (!toggleOs.checked && Math.random() > 0.94) { stutter = 12 + Math.random() * 8; // Random latency spike } const nextPoint = targetLat + (Math.random() - 0.5) * noiseLevel + stutter; points.shift(); points.push(nextPoint); // 3. Draw Line Graph ctx.beginPath(); // Calculate step size const step = width / (maxPoints - 1); // Map values to height (latency between 0ms and 40ms) function mapValToY(val) { // Limit range to prevent drawing out of bounds const clamped = Math.max(2, Math.min(38, val)); // Inverse because canvas (0,0) is top-left return height - (clamped / 40) * height * 0.85 - 10; } // Draw line for (let i = 0; i < points.length; i++) { const x = i * step; const y = mapValToY(points[i]); if (i === 0) { ctx.moveTo(x, y); } else { // Smooth curve using quadratic control points const prevX = (i - 1) * step; const prevY = mapValToY(points[i - 1]); const cpX = (prevX + x) / 2; ctx.quadraticCurveTo(cpX, prevY, cpX, (prevY + y) / 2); ctx.lineTo(x, y); } } // Determine line color and shadow depending on optimizations let glowColor, strokeStyle; const activeCount = (toggleRam.checked ? 1 : 0) + (toggleCpu.checked ? 1 : 0) + (toggleOs.checked ? 1 : 0); if (isLight) { if (activeCount === 3) { strokeStyle = '#00aa00'; // Forest green glowColor = 'rgba(0, 170, 0, 0.15)'; } else if (activeCount === 0) { strokeStyle = '#d90429'; // Solid red glowColor = 'rgba(217, 4, 41, 0.15)'; } else { strokeStyle = '#0088cc'; // Deep cyan glowColor = 'rgba(0, 136, 204, 0.15)'; } } else { if (activeCount === 3) { strokeStyle = '#39ff14'; // Neon Green glowColor = 'rgba(57, 255, 20, 0.3)'; } else if (activeCount === 0) { strokeStyle = '#ff3366'; // Neon Red glowColor = 'rgba(255, 51, 102, 0.3)'; } else { strokeStyle = '#00ffea'; // Neon Cyan glowColor = 'rgba(0, 255, 234, 0.3)'; } } ctx.strokeStyle = strokeStyle; ctx.lineWidth = 2.5; ctx.shadowBlur = 10; ctx.shadowColor = strokeStyle; ctx.stroke(); // Reset shadows for filling the bottom area ctx.shadowBlur = 0; // 4. Fill path underneath line for glow area ctx.lineTo(width, height); ctx.lineTo(0, height); ctx.closePath(); const fillGrad = ctx.createLinearGradient(0, 0, 0, height); fillGrad.addColorStop(0, glowColor); fillGrad.addColorStop(1, 'rgba(0, 0, 0, 0)'); ctx.fillStyle = fillGrad; ctx.fill(); setTimeout(drawGraph, 50); // Control update speed (20 FPS is smooth enough for scope) } // Start graph rendering drawGraph(); } /* ================================================== 7. Contact Form Simulation & Toast Notifications ================================================== */ const contactForm = document.getElementById('portfolio-contact-form'); const toastContainer = document.getElementById('toast-container'); function showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `toast ${type === 'success' ? 'toast-success' : 'toast-info'}`; let iconClass = 'fa-solid fa-circle-check'; if (type === 'info') { iconClass = 'fa-solid fa-circle-info text-accent'; } toast.innerHTML = ` <i class="${iconClass} toast-icon"></i> <span>${message}</span> `; toastContainer.appendChild(toast); // Remove toast after animation setTimeout(() => { toast.classList.add('fade-out'); toast.addEventListener('animationend', () => { toast.remove(); }); }, 3500); } if (contactForm) { contactForm.addEventListener('submit', (e) => { e.preventDefault(); const btnSubmit = contactForm.querySelector('.btn-submit'); const btnText = btnSubmit.querySelector('.btn-text'); const btnIcon = btnSubmit.querySelector('.btn-icon'); // Set sending animation state btnSubmit.disabled = true; btnText.textContent = 'กำลังส่งข้อความ (Sending...)'; btnIcon.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>'; const name = document.getElementById('form-name').value; // Simulate server network latency setTimeout(() => { // Success feedback showToast(`ส่งข้อความเรียบร้อยแล้ว! ขอบคุณครับคุณ ${name}.`, 'success'); // Reset form values & labels contactForm.reset(); // Re-enable submit button btnSubmit.disabled = false; btnText.textContent = 'ส่งข้อความ (Send Message)'; btnIcon.innerHTML = '<i class="fa-solid fa-paper-plane"></i>'; }, 1800); }); } });
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.36 |
proxy
|
phpinfo
|
Settings