File manager - Edit - /home/webapp69.cm.in.th/u69319090017/69319090017/app.js
Back
// Portfolio JavaScript Logic - Phumin Kanokhathaiphong (Min) // Updated: 2026 // 0. Initialize & Persistent Theme Configuration window.customizeColor = (colorHex) => { const root = document.documentElement; const isLight = root.classList.contains('light-theme'); if (isLight) { root.style.setProperty('--brand-accent', colorHex); } else { root.style.setProperty('--brand-accent', '#ffffff'); } root.style.setProperty('--brand-secondary', colorHex); root.style.setProperty('--brand-glow-color', isLight ? 'transparent' : colorHex); root.style.setProperty('--brand-text-glow', isLight ? 'transparent' : colorHex + '55'); // Glow transparency root.setAttribute('data-theme', 'custom'); localStorage.setItem('portfolio-theme', 'custom'); localStorage.setItem('custom-brand-color', colorHex); const picker = document.getElementById("custom-color-picker"); if (picker) { picker.value = colorHex; } }; window.customizeTextColor = (colorHex) => { const root = document.documentElement; root.style.setProperty('--brand-primary', colorHex); root.setAttribute('data-theme', 'custom'); localStorage.setItem('portfolio-theme', 'custom'); localStorage.setItem('custom-text-color', colorHex); const picker = document.getElementById("custom-text-picker"); if (picker) { picker.value = colorHex; } }; window.customizeBgColor = (colorHex) => { const root = document.documentElement; root.style.setProperty('--brand-deep', colorHex); // Check if background is light or dark (YIQ relative luminance formula) let hex = colorHex.replace('#', ''); if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } const r = parseInt(hex.substr(0, 2), 16); const g = parseInt(hex.substr(2, 2), 16); const b = parseInt(hex.substr(4, 2), 16); const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; const isLight = yiq >= 128; if (isLight) { root.classList.add('light-theme'); root.style.setProperty('--brand-card', `color-mix(in srgb, ${colorHex} 94%, black 6%)`); root.style.setProperty('--brand-border', `color-mix(in srgb, ${colorHex} 86%, black 14%)`); const customText = localStorage.getItem('custom-text-color'); root.style.setProperty('--brand-primary', customText || '#0f172a'); root.style.setProperty('--brand-accent', '#0284c7'); root.style.setProperty('--brand-glow-color', 'transparent'); root.style.setProperty('--brand-text-glow', 'transparent'); } else { root.classList.remove('light-theme'); root.style.setProperty('--brand-card', `color-mix(in srgb, ${colorHex} 94%, white 6%)`); root.style.setProperty('--brand-border', `color-mix(in srgb, ${colorHex} 86%, white 14%)`); const customColor = localStorage.getItem('custom-brand-color') || '#ffffff'; const customText = localStorage.getItem('custom-text-color'); root.style.setProperty('--brand-primary', customText || '#ffffff'); root.style.setProperty('--brand-accent', '#ffffff'); root.style.setProperty('--brand-glow-color', customColor); root.style.setProperty('--brand-text-glow', customColor + '55'); } root.setAttribute('data-theme', 'custom'); localStorage.setItem('portfolio-theme', 'custom'); localStorage.setItem('custom-bg-color', colorHex); const picker = document.getElementById("custom-bg-picker"); if (picker) { picker.value = colorHex; } }; (() => { const root = document.documentElement; const savedTheme = localStorage.getItem('portfolio-theme') || 'mint'; root.setAttribute('data-theme', savedTheme); if (savedTheme === 'custom') { const customColor = localStorage.getItem('custom-brand-color') || '#ffffff'; const customBg = localStorage.getItem('custom-bg-color') || '#000000'; const customText = localStorage.getItem('custom-text-color'); setTimeout(() => { window.customizeBgColor(customBg); window.customizeColor(customColor); if (customText) { window.customizeTextColor(customText); } }, 50); } else { root.classList.remove('light-theme'); } })(); // 1. Data Models (ข้อมูลส่วนตัว ประวัติ และไลฟ์สไตล์ของคุณมินทร์) const skills = [ 'ชอบกิน (Food Enthusiast)', 'เล่นเกม (Gaming)', 'ฟังเพลง (Music Lover)', 'นอนหลับ (Rest & Sleep)', 'ทำอาหาร (Cooking)' ]; const hobbies = [ { title: "ชอบกิน (Eating)", desc: "ตระเวนชิมอาหารอร่อยๆ ค้นหาร้านเด็ดๆ เพื่อสัมผัสรสชาติใหม่ๆ เป็นการเติมพลังงานและความสุขให้กับชีวิตในทุกๆ วัน", tech: "Food & Eating", image: "https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=600&q=80", link: "#" }, { title: "เล่นเกม (Gaming)", desc: "กิจกรรมผ่อนคลายความเครียดในเวลาว่าง ช่วยฝึกทักษะการตัดสินใจ การแก้ปัญหาเฉพาะหน้า และการทำงานร่วมกันเป็นทีม", tech: "Entertainment • Skills", image: "https://images.unsplash.com/photo-1538481199705-c710c4e965fc?w=600&q=80", link: "#" }, { title: "ฟังเพลง (Music Lover)", desc: "เสียงดนตรีช่วยบำบัดและสร้างแรงบันดาลใจ ฟังเพลงได้หลากหลายสไตล์เพื่อปรับอารมณ์และสร้างสมาธิในยามอ่านหนังสือหรือเขียนโค้ด", tech: "Relaxation", image: "https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?w=600&q=80", link: "#" }, { title: "นอนหลับ (Sleeping)", desc: "ให้ความสำคัญกับการนอนหลับพักผ่อนอย่างเต็มที่ เพราะการนอนคือการฟื้นฟูร่างกายและสมองที่ดีที่สุดเพื่อให้พร้อมเริ่มวันใหม่อย่างสดใส", tech: "Health & Mindset", image: "https://images.unsplash.com/photo-1511295742364-5a633553028a?w=600&q=80", link: "#" }, { title: "ทำอาหาร (Cooking)", desc: "สนุกกับการเข้าครัวปรุงอาหารด้วยตัวเอง รังสรรค์เมนูที่ต้องการทาน คัดสรรวัตถุดิบและปรับปรุงสูตรตามสไตล์ที่ชื่นชอบ", tech: "Culinary Arts", image: "https://images.unsplash.com/photo-1556910103-1c02745aae4d?w=600&q=80", link: "#" } ]; // 2. Theme Switching Logic with Ripple Transition Animation window.switchTheme = (themeName, event) => { const root = document.documentElement; // Clear any light theme properties and custom theme overrides root.classList.remove('light-theme'); root.style.removeProperty('--brand-deep'); root.style.removeProperty('--brand-card'); root.style.removeProperty('--brand-border'); root.style.removeProperty('--brand-primary'); root.style.removeProperty('--brand-accent'); root.style.removeProperty('--brand-secondary'); root.style.removeProperty('--brand-glow-color'); root.style.removeProperty('--brand-text-glow'); const currentTheme = root.getAttribute('data-theme') || 'mint'; if (currentTheme === themeName) return; if (!event) { root.setAttribute('data-theme', themeName); localStorage.setItem('portfolio-theme', themeName); return; } // Circular reveal ripple animation const x = event.clientX; const y = event.clientY; const endRadius = Math.hypot( Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y) ); // Create temporary ripple overlay const overlay = document.createElement('div'); overlay.style.position = 'fixed'; overlay.style.left = x + 'px'; overlay.style.top = y + 'px'; overlay.style.width = '0px'; overlay.style.height = '0px'; overlay.style.borderRadius = '50%'; overlay.style.transform = 'translate(-50%, -50%)'; overlay.style.zIndex = '9999'; overlay.style.pointerEvents = 'none'; // Get theme target background color (All themes are black bg) let targetBg = '#000000'; overlay.style.backgroundColor = targetBg; overlay.style.transition = 'all 0.65s cubic-bezier(0.4, 0, 0.2, 1)'; document.body.appendChild(overlay); // Force reflow overlay.offsetWidth; // Animate growth overlay.style.width = (endRadius * 2) + 'px'; overlay.style.height = (endRadius * 2) + 'px'; overlay.addEventListener('transitionend', () => { root.setAttribute('data-theme', themeName); localStorage.setItem('portfolio-theme', themeName); // Smoothly fade out overlay overlay.style.transition = 'opacity 0.4s ease'; overlay.style.opacity = '0'; setTimeout(() => { overlay.remove(); }, 400); }); }; // 3. DOM Elements Rendering document.addEventListener("DOMContentLoaded", () => { // เปลี่ยนชื่อ Web Title ทันทีเมื่อหน้าโหลดเสร็จ document.title = "MIN PHUMIN | Portfolio"; // Render Skills const skillsContainer = document.getElementById("skills-container"); if (skillsContainer) { skillsContainer.innerHTML = ""; skills.forEach(skill => { const pill = document.createElement("span"); pill.className = "skill-pill px-4 py-2 bg-brand-primary/10 border border-brand-primary/30 text-brand-accent rounded-full text-sm font-medium hover:bg-gradient-to-r hover:from-brand-primary hover:to-brand-secondary hover:text-slate-900 cursor-default transition-all duration-300 shadow-sm"; pill.textContent = skill; skillsContainer.appendChild(pill); }); } // Render Hobbies in projects grid const projectsGrid = document.getElementById("projects-grid"); if (projectsGrid) { projectsGrid.innerHTML = ""; hobbies.forEach(h => { const card = document.createElement("div"); card.className = "project-card group bg-brand-card border border-brand-border/80 rounded-2xl overflow-hidden shadow-lg transition-all duration-300 flex flex-col justify-between hover:border-brand-primary/40"; card.innerHTML = ` <div> <div class="h-52 overflow-hidden relative"> <div class="absolute inset-0 bg-gradient-to-t from-brand-deep via-transparent to-transparent opacity-70 z-10"></div> <img src="${h.image}" alt="${h.title}" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700 ease-out" /> </div> <div class="p-6"> <span class="text-brand-primary text-xs font-bold uppercase tracking-wider">${h.tech}</span> <h3 class="text-xl font-bold mt-2 mb-3 text-slate-100">${h.title}</h3> <p class="text-slate-400 text-sm leading-relaxed font-light">${h.desc}</p> </div> </div> <div class="p-6 pt-0"> <a href="${h.link}" class="inline-flex items-center gap-1.5 text-sm font-bold text-brand-primary hover:text-brand-accent group-hover:underline transition-colors mt-4"> อ่านเพิ่มเติม <i data-lucide="arrow-up-right" class="w-4 h-4 text-brand-primary"></i> </a> </div> `; projectsGrid.appendChild(card); }); } // Initialize Lucide Icons if (window.lucide) { window.lucide.createIcons(); } }); // 4. Navigation Controls const menuBtn = document.getElementById("menu-btn"); const mobileMenu = document.getElementById("mobile-menu"); const navbar = document.getElementById("navbar"); if (menuBtn && mobileMenu) { menuBtn.addEventListener("click", () => { mobileMenu.classList.toggle("hidden"); const icon = menuBtn.querySelector("i"); if (icon) { const isOpen = !mobileMenu.classList.contains("hidden"); icon.setAttribute("data-lucide", isOpen ? "x" : "menu"); if (window.lucide) window.lucide.createIcons(); } }); document.querySelectorAll(".mobile-nav-link").forEach(link => { link.addEventListener("click", () => { mobileMenu.classList.add("hidden"); const icon = menuBtn.querySelector("i"); if (icon) { icon.setAttribute("data-lucide", "menu"); if (window.lucide) window.lucide.createIcons(); } }); }); } // 5. Navbar State on Scroll window.addEventListener("scroll", () => { if (window.scrollY > 50) { navbar.classList.add("bg-brand-deep/95", "shadow-xl"); navbar.classList.remove("bg-brand-deep/80"); } else { navbar.classList.add("bg-brand-deep/80"); navbar.classList.remove("bg-brand-deep/95", "shadow-xl"); } }); // 6. Contact Form Submission const contactForm = document.getElementById("contact-form"); const toast = document.getElementById("toast"); if (contactForm && toast) { contactForm.addEventListener("submit", (e) => { e.preventDefault(); const btn = contactForm.querySelector("button[type='submit']"); const originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = `<span class="inline-flex items-center gap-2 justify-center"><svg class="animate-spin h-5 w-5 text-slate-900" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> กำลังส่งข้อความ...</span>`; setTimeout(() => { btn.innerHTML = originalText; btn.disabled = false; contactForm.reset(); toast.classList.remove("translate-y-24", "opacity-0"); toast.classList.add("translate-y-0", "opacity-100"); setTimeout(() => { toast.classList.remove("translate-y-0", "opacity-100"); toast.classList.add("translate-y-24", "opacity-0"); }, 4000); }, 1200); }); } // 7. Mobile Sticky Bottom Nav & Bottom Sheet Drawer Logic const mobileBurgerBtn = document.getElementById("mobile-burger-btn"); const mobileBottomSheet = document.getElementById("mobile-bottom-sheet"); const mobileBottomSheetContent = document.getElementById("mobile-bottom-sheet-content"); const mobileBottomSheetClose = document.getElementById("mobile-bottom-sheet-close"); const burgerIconIcon = document.getElementById("burger-icon-icon"); function openBottomSheet() { if (mobileBottomSheet && mobileBottomSheetContent) { mobileBottomSheet.classList.remove("opacity-0", "pointer-events-none"); mobileBottomSheetContent.classList.remove("translate-y-full"); mobileBottomSheetContent.classList.add("translate-y-0"); if (burgerIconIcon) { burgerIconIcon.setAttribute("data-lucide", "x"); burgerIconIcon.classList.add("rotate-90"); if (window.lucide) window.lucide.createIcons(); } } } function closeBottomSheet() { if (mobileBottomSheet && mobileBottomSheetContent) { mobileBottomSheet.classList.add("opacity-0", "pointer-events-none"); mobileBottomSheetContent.classList.remove("translate-y-0"); mobileBottomSheetContent.classList.add("translate-y-full"); if (burgerIconIcon) { burgerIconIcon.setAttribute("data-lucide", "menu"); burgerIconIcon.classList.remove("rotate-90"); if (window.lucide) window.lucide.createIcons(); } } } if (mobileBurgerBtn && mobileBottomSheet) { mobileBurgerBtn.addEventListener("click", (e) => { e.stopPropagation(); const isOpen = !mobileBottomSheet.classList.contains("opacity-0"); if (isOpen) { closeBottomSheet(); } else { openBottomSheet(); } }); } if (mobileBottomSheetClose) { mobileBottomSheetClose.addEventListener("click", closeBottomSheet); } // Close bottom sheet when clicking outside the content if (mobileBottomSheet) { mobileBottomSheet.addEventListener("click", (e) => { if (mobileBottomSheetContent && !mobileBottomSheetContent.contains(e.target)) { closeBottomSheet(); } }); } // Close when clicking any link inside the bottom sheet document.querySelectorAll(".mobile-sheet-link").forEach(link => { link.addEventListener("click", closeBottomSheet); }); // 8. Active Link Highlighting on Scroll for Sticky Bottom Nav const sections = document.querySelectorAll("section[id]"); const bottomNavLinks = document.querySelectorAll(".mobile-bottom-nav-link"); function highlightBottomNav() { let scrollY = window.pageYOffset || document.documentElement.scrollTop; sections.forEach(current => { const sectionHeight = current.offsetHeight; const sectionTop = current.offsetTop - 200; const sectionId = current.getAttribute("id"); if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) { bottomNavLinks.forEach(link => { if (link.getAttribute("href") === `#${sectionId}`) { link.classList.remove("text-slate-400"); link.classList.add("text-brand-primary", "scale-105"); } else { link.classList.add("text-slate-400"); link.classList.remove("text-brand-primary", "scale-105"); } }); } }); } window.addEventListener("scroll", highlightBottomNav); window.addEventListener("load", highlightBottomNav); highlightBottomNav();
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.34 |
proxy
|
phpinfo
|
Settings