PHP Code Editor
<?php // ========================================================================= // --- CONFIGURATION --- // ========================================================================= $uploadDir = 'uploads/'; $dataFile = 'gallery.txt'; $allowedTypes = ['jpg', 'jpeg', 'png', 'gif']; $maxSize = 5 * 1024 * 1024; // 5 MB // Ukuran Gambar Utama (HD) $targetWidth = 1920; $targetHeight = 1080; // Ukuran Thumbnail (Baru) $thumbWidth = 320; // Lebar thumbnail $thumbHeight = 180; // Tinggi thumbnail (Rasio 16:9) $message = ''; $error = ''; // Server settings adjustment @ini_set('upload_max_filesize', '5M'); @ini_set('post_max_size', '16M'); // --- Configuration Pagination --- $imagesPerPage = 12; $currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1; if ($currentPage < 1) $currentPage = 1; // Ensure upload directory exists if (!is_dir($uploadDir)) { if (!mkdir($uploadDir, 0755, true)) { die("Fatal Error: Failed to create upload directory."); } } // ========================================================================= // --- IMAGE FILE LOADER (HANDLER UNTUK ?fldr=...&file=...) --- // ========================================================================= // Bagian ini menangani permintaan gambar agar tidak direct access if (isset($_GET['fldr']) && isset($_GET['file'])) { // Security: Bersihkan input untuk mencegah Directory Traversal $requestedDir = basename($_GET['fldr']); $requestedFile = basename($_GET['file']); // Pastikan folder yang diminta adalah folder upload yang valid // Kita memaksa hanya bisa membaca dari $uploadDir yang dikonfigurasi $cleanUploadDir = trim($uploadDir, '/'); if ($requestedDir === $cleanUploadDir) { $filePath = $uploadDir . $requestedFile; if (file_exists($filePath)) { // Dapatkan info gambar $imageInfo = getimagesize($filePath); if ($imageInfo !== false) { // Kirim Header Gambar header("Content-Type: " . $imageInfo['mime']); header('Content-Disposition: inline; filename="' . basename($filePath) . '"'); header("Content-Length: " . filesize($filePath)); // Optional: Cache control untuk performa browser header("Cache-Control: public, max-age=86400"); // Baca dan kirim file readfile($filePath); exit; } else { header("HTTP/1.0 404 Not Found"); exit("Invalid image file."); } } else { header("HTTP/1.0 404 Not Found"); exit("File not found."); } } else { header("HTTP/1.0 403 Forbidden"); exit("Access denied."); } } // ========================================================================= // --- FUNGSI BANTUAN --- // ========================================================================= function getAllImageFiles($file) { if (file_exists($file)) { $files = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); return array_reverse($files); } return []; } // FUNGSI RESIZE DAN CROP function resizeAndCropImage($sourceFile, $destinationFile, $targetW, $targetH, $mimeType) { if (!extension_loaded('gd')) { error_log("GD extension is not loaded."); return false; } switch ($mimeType) { case 'image/jpeg': $image = imagecreatefromjpeg($sourceFile); break; case 'image/png': $image = imagecreatefrompng($sourceFile); break; case 'image/gif': $image = imagecreatefromgif($sourceFile); break; default: return false; } if (!$image) return false; $sourceWidth = imagesx($image); $sourceHeight = imagesy($image); $targetRatio = $targetW / $targetH; $sourceRatio = $sourceWidth / $sourceHeight; if ($sourceRatio > $targetRatio) { $tempHeight = $sourceHeight; $tempWidth = $sourceHeight * $targetRatio; $sourceX = ($sourceWidth - $tempWidth) / 2; $sourceY = 0; } else { $tempWidth = $sourceWidth; $tempHeight = $sourceWidth / $targetRatio; $sourceX = 0; $sourceY = ($sourceHeight - $tempHeight) / 2; } $targetImage = imagecreatetruecolor($targetW, $targetH); if ($mimeType == 'image/png' || $mimeType == 'image/gif') { imagecolortransparent($targetImage, imagecolorallocatealpha($targetImage, 0, 0, 0, 127)); imagealphablending($targetImage, false); imagesavealpha($targetImage, true); } if (!imagecopyresampled($targetImage, $image, 0, 0, $sourceX, $sourceY, $targetW, $targetH, $tempWidth, $tempHeight)) { imagedestroy($image); imagedestroy($targetImage); return false; } $success = false; switch ($mimeType) { case 'image/jpeg': $success = imagejpeg($targetImage, $destinationFile, 90); break; case 'image/png': $success = imagepng($targetImage, $destinationFile); break; case 'image/gif': $success = imagegif($targetImage, $destinationFile); break; } imagedestroy($image); imagedestroy($targetImage); return $success; } // ========================================================================= // --- HANDLE ACTIONS (DELETE & UPLOAD) --- // ========================================================================= // --- DELETE --- if (isset($_POST['delete_image']) && isset($_POST['filename'])) { $fileToDelete = basename($_POST['filename']); $filePath = $uploadDir . $fileToDelete; // Tentukan path thumbnail untuk dihapus juga $thumbPath = $uploadDir . 'thmb_' . $fileToDelete; if (file_exists($filePath)) { if (unlink($filePath)) { // Hapus thumbnail jika ada if (file_exists($thumbPath)) { unlink($thumbPath); } // Hapus nama file dari gallery.txt $allFiles = getAllImageFiles($dataFile); $newFiles = array_filter($allFiles, function($file) use ($fileToDelete) { return $file !== $fileToDelete; }); file_put_contents($dataFile, implode("\n", array_reverse($newFiles)) . "\n", LOCK_EX); $message = "Image '{$fileToDelete}' deleted successfully!"; $returnPage = isset($_POST['page']) ? (int)$_POST['page'] : 1; header("Location: " . strtok($_SERVER["REQUEST_URI"], '?') . "?page={$returnPage}&msg=" . urlencode($message)); exit; } else { $error = "Failed to delete file from disk."; } } else { $error = "File not found."; } } // --- UPLOAD --- if (isset($_POST['submit'])) { if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $fileName = basename($_FILES['image']['name']); $fileTmpName = $_FILES['image']['tmp_name']; $fileSize = $_FILES['image']['size']; $fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $uniqueFileName = uniqid() . '.' . $fileExtension; $tempDestination = $uploadDir . 'temp_' . $uniqueFileName; // Nama file tujuan (Utama dan Thumbnail) $finalDestination = $uploadDir . $uniqueFileName; $thumbDestination = $uploadDir . 'thmb_' . $uniqueFileName; if (!in_array($fileExtension, $allowedTypes)) { $error = 'Invalid file type.'; } elseif ($fileSize > $maxSize) { $error = 'File size exceeds limit. Max: ' . round($maxSize / 1024 / 1024) . ' MB'; } else { if (move_uploaded_file($fileTmpName, $tempDestination)) { $imageInfo = getimagesize($tempDestination); if ($imageInfo === false) { $error = 'Could not get image info.'; unlink($tempDestination); } else { $mimeType = $imageInfo['mime']; // 1. Proses Gambar Utama (Original/HD) $processMain = resizeAndCropImage($tempDestination, $finalDestination, $targetWidth, $targetHeight, $mimeType); // 2. Proses Thumbnail (Kecil) $processThumb = resizeAndCropImage($tempDestination, $thumbDestination, $thumbWidth, $thumbHeight, $mimeType); if ($processMain && $processThumb) { unlink($tempDestination); // Hapus file temp file_put_contents($dataFile, $uniqueFileName . "\n", FILE_APPEND | LOCK_EX); $message = 'Image uploaded and thumbnail created successfully!'; header("Location: " . strtok($_SERVER["REQUEST_URI"], '?') . "?page=1&msg=" . urlencode($message)); exit; } else { $error = 'Failed to process image resizing.'; unlink($tempDestination); } } } else { $error = 'Failed to move uploaded file.'; } } } else if (isset($_POST['submit'])) { $error = (isset($_FILES['image']['error']) && $_FILES['image']['error'] !== UPLOAD_ERR_NO_FILE) ? "Upload Error: {$_FILES['image']['error']}" : 'No file selected.'; } } if (isset($_GET['msg'])) { $message = htmlspecialchars($_GET['msg']); } // --- PAGINATION LOGIC --- $allImageFiles = getAllImageFiles($dataFile); $totalImages = count($allImageFiles); $totalPages = ceil($totalImages / $imagesPerPage); if ($totalPages > 0 && $currentPage > $totalPages) { $currentPage = $totalPages; } $offset = ($currentPage - 1) * $imagesPerPage; $imageFiles = array_slice($allImageFiles, $offset, $imagesPerPage); // ========================================================================= // --- RANDOM IMAGE LOADER (LEGACY/API) --- // ========================================================================= if(isset($_GET['rndimg'])) { $expirationTime = 3; header("HTTP/1.1 200 OK"); header("Status: 200 OK"); header("Cache-Control: max-age={$expirationTime}, no-cache, must-revalidate"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Pragma: no-cache"); if (!file_exists($dataFile)) { header("HTTP/1.0 404 Not Found"); exit("Error: Data file not found."); } $lines = file($dataFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if (empty($lines)) { header("HTTP/1.0 404 Not Found"); exit("Error: Data file is empty."); } $randomIndex = array_rand($lines); $randomLine = $lines[$randomIndex]; $imagePath = $uploadDir . $randomLine; if (!file_exists($imagePath)) { header("HTTP/1.0 404 Not Found"); exit("Image not found."); } $imageInfo = getimagesize($imagePath); if ($imageInfo === false) { header("HTTP/1.0 500 Internal Server Error"); exit; } header("Content-Type: " . $imageInfo['mime']); header("Access-Control-Allow-Origin: *"); readfile($imagePath); header("HTTP/1.1 307 Temporary Redirect", FALSE, 307); header("Location: $imagePath"); exit(); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Image Gallery</title> <link rel="icon" href="https://nxcbmd.my.id/apps/theming/favicon?v=37"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> /* CSS Dasar */ body { background-color: #ffffff; color: #333333; font-family: sans-serif; padding: 15px; transition: background-color 0.3s, color 0.3s; } body.dark-mode { background-color: #242424; color: #9e9e9e; } .dark-mode .gallery-item { background-color: #333333; border: 1px solid #555; } .dark-mode .gallery-item img { border: 1px solid #555; } .dark-mode .pagination a, .dark-mode .pagination span { border: 1px solid #555; color: #9e9e9e; background-color: #333333; } .dark-mode .pagination a:hover { background-color: #444; } .dark-mode .pagination .current-page { background-color: #007bff; color: white; border-color: #007bff; } #theme-toggle { position: fixed; top: 10px; right: 15px; padding: 10px; cursor: pointer; border: 1px solid #ccc; background-color: #f0f0f0; color: #333; border-radius: 5px; font-weight: bold; transition: all 0.3s ease-in-out; width: 40px; overflow: hidden; white-space: nowrap; text-align: center; } .dark-mode #theme-toggle { background-color: #333; color: #fff; border-color: #555; } #theme-toggle:hover { width: 128px; padding: 10px 15px; text-align: left; } #theme-toggle .icon { display: inline-block; width: 20px; transition: transform 0.3s; } #theme-toggle .text { margin-left: 5px; opacity: 0; transition: opacity 0.3s 0.1s; } #theme-toggle:hover .text { opacity: 1; } .gallery { display: flex; flex-wrap: wrap; gap: 15px; max-width: 1111px; } .gallery-item { display: flex; flex-direction: column; align-items: center; border: 1px solid #454547; padding: 7px; width: 147px; box-sizing: border-box; background-color: #f8f8f8; border-radius: 3px; transition: background-color 0.3s, border 0.3s; box-shadow: rgba(112, 128, 186, 0.32) 3px 3px 5px; } .gallery-item img { width: 130px; height: 73px; object-fit: cover; margin-bottom: 10px; border: 1px solid #434345; transition: border 0.25s; cursor: pointer; /* MODIFIKASI: Transisi untuk transform (smooth scale) */ transition: transform 0.25s ease-in-out; } /* MODIFIKASI: Efek Scale 1.037 saat hover */ .gallery-item img:hover { transform: scale(1.037); z-index: 10; /* Pastikan gambar yang di-hover muncul di atas */ box-shadow: 0 0 10px rgba(0, 0, 0, 0.37); /* Opsional: Tambah shadow */ } /* MODIFIKASI: Pastikan tidak ada overflow saat scale */ .gallery-item { overflow: hidden; /* Tambahkan ini agar gambar yang di-zoom tidak keluar dari item */ } .message { color: green; font-weight: bold; } .error { color: red; font-weight: bold; } .pagination { margin: 20px 0; display: flex; gap: 10px; } .pagination a, .pagination span { padding: 8px 15px; text-decoration: none; border: 1px solid #ccc; color: #888; border-radius: 5px; transition: all 0.3s; } .pagination a:hover { background-color: #eee; } .pagination .current-page { background-color: #007bff; color: white; border-color: #007bff; font-weight: bold; } .delete-form { margin-top: 5px; } .delete-form button { background-color: #dc3545; color: white; border: none; padding: 3px 7px; cursor: pointer; border-radius: 3px; } .delete-form button:hover { background-color: #c82333; } /* MODAL / LIGHTBOX */ .modal { display: none; position: fixed; z-index: 1000; padding-top: 50px; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.97); } /* Modifikasi: Tambahkan transisi transform ke modal-content */ .modal-content { margin: auto; display: block; width: 90%; max-width: 997px; max-height: 90vh; object-fit: contain; /* Tambahkan transition untuk transform dan opacity */ transition: transform 0.5s ease-out, opacity 0.5s ease-out; } /* --- MODIFIKASI: Kelas untuk efek fade-out saat menutup --- */ .modal.closing .modal-content { /* Memicu animasi fade-out */ opacity: 0.17 !important; } /* ------------------------------------------------------------------- */ /* --- MODIFIKASI: Tambahkan transisi fading pada gambar modal (sudah ada) --- */ #img01 { opacity: 1; transition: opacity 0.27s ease-in-out; /* Transisi 0.27 detik untuk efek pudar */ } #img01.fade-out { /* Kelas yang akan digunakan sebelum ganti src */ opacity: 0.27; } /* ------------------------------------------------------------------- */ #caption { margin: auto; display: block; width: 80%; max-width: 977px; text-align: center; color: #ccc; padding: 10px 0; height: 107px; } .prev, .next { cursor: pointer; position: absolute; top: 50%; width: auto; padding: 32px; margin-top: -130px; color: white; font-weight: bold; font-size: 32px; transition: 0.3s ease; border-radius: 0 3px 3px 0; user-select: none; } .next { right: 0; border-radius: 3px 0 0 3px; } .prev:hover, .next:hover { background-color: rgba(77, 77, 77, 0.7); } .close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; transition: 0.3s; cursor: pointer; } .close:hover { color: #888; text-decoration: none; } /* MODIFIKASI: Tambahkan keyframe untuk animasi pembuka (zoom-in) */ @keyframes zoom-in { from {transform:scale(0.3); opacity: 0.3;} to {transform:scale(1); opacity: 1;} } /* Modifikasi: Ganti animasi bawaan modal-content */ .modal-content { /* Hilangkan animasi lama, ganti dengan transisi CSS */ animation: zoom-in 0.5s; } /* --- CSS OVERLAY BARU --- */ #loading-overlay { display: none; /* Awalnya disembunyikan */ position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); /* Semi-transparan hitam */ z-index: 9999; /* Pastikan di atas semua elemen lain */ backdrop-filter: blur(2px); /* Efek blur ringan */ justify-content: center; align-items: center; flex-direction: column; color: white; font-size: 20px; font-weight: bold; } .spinner { border: 8px solid #f3f3f3; border-top: 8px solid #3498db; border-radius: 50%; width: 60px; height: 60px; animation: spin 1s linear infinite; margin-bottom: 20px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> </head> <body> <div id="loading-overlay"> <div class="spinner"></div> <p>Uploading & Processing Image... Please wait.</p> <p style="font-size: 14px; font-weight: normal; margin-top: 5px;">This may take a moment due to resizing.</p> </div> <button id="theme-toggle"> <span class="icon">☀️</span><span class="text">Light Mode</span> </button> <a href="?" style="text-decoration: none; color: #9e9e9e;"><h2>🖼️ Upload Image</h2></a> <?php if ($message): ?> <p class="message"><?php echo $message; ?></p> <?php endif; ?> <?php if ($error): ?> <p class="error"><?php echo $error; ?></p> <?php endif; ?> <form action="" method="post" enctype="multipart/form-data" id="uploadForm" onsubmit="return confirmUpload();"> <input type="file" name="image" accept=".jpg, .jpeg, image/jpeg" required="" style="background-color: #007BFF; /* Warna latar hijau estetik */ width: 297px; border: none; /* Hilangkan border */ color: white; /* Warna teks putih */ padding: 3px 7px; /* Jarak padding */ text-align: center; /* Teks di tengah */ text-decoration: none; /* Hilangkan garis bawah */ display: inline-block; font-size: 11.5px; margin: 3px 1px; cursor: pointer; /* Ubah kursor menjadi pointer saat diarahkan */ border-radius: 8px; /* Sudut membulat */ box-shadow: 0 3px 7px 0 rgba(0,0,0,0.2); /* Bayangan lembut */ transition-duration: 0.3s; /* Transisi untuk efek hover */"> <button type="submit" name="submit" style="background-color: #007BFF; /* Warna latar hijau estetik */ border: none; /* Hilangkan border */ color: white; /* Warna teks putih */ padding: 5px 11px; /* Jarak padding */ text-align: center; /* Teks di tengah */ text-decoration: none; /* Hilangkan garis bawah */ display: inline-block; font-size: 13px; margin: 4px 2px; cursor: pointer; /* Ubah kursor menjadi pointer saat diarahkan */ border-radius: 7px; /* Sudut membulat */ box-shadow: 0 3px 7px 0 rgba(0,0,0,0.2); /* Bayangan lembut */ transition-duration: 0.3s; /* Transisi untuk efek hover */">Upload & Process Image</button> </form> <hr> <h3 style="color: #9e9e9e;">📸 Image Gallery (Page <?php echo $currentPage; ?>)</h3> <?php if (empty($imageFiles)): ?> <p>Galeri kosong atau halaman tidak ditemukan.</p> <?php endif; ?> <div class="gallery"> <?php // Array untuk menyimpan URL loader (bukan path file langsung) $allImageUrls = []; $cleanUploadDirName = trim($uploadDir, '/'); // "uploads" foreach ($imageFiles as $image) { // URL Loader untuk Gambar UTAMA (Full Size) // Format: ./?fldr=uploads&file=NamaFile.jpg $fullImageUrl = "?fldr=" . $cleanUploadDirName . "&file=" . urlencode($image); // URL Loader untuk THUMBNAIL // Format: ./?fldr=uploads&file=thmb_NamaFile.jpg // Kita cek dulu apa file thumbnail ada (untuk backward compatibility gambar lama) if (file_exists($uploadDir . 'thmb_' . $image)) { $thumbImageUrl = "?fldr=" . $cleanUploadDirName . "&file=" . urlencode('thmb_' . $image); } else { // Fallback ke gambar utama jika thumbnail tidak ada (file lama) $thumbImageUrl = $fullImageUrl; } $allImageUrls[] = $fullImageUrl; ?> <div class="gallery-item"> <img src="<?php echo $thumbImageUrl; ?>" alt="Gallery Image" onclick="openModal(this)" data-fullsize-url="<?php echo $fullImageUrl; ?>"> <p style="font-size: small; margin: 0; word-break: break-all;"><?php echo htmlspecialchars($image); ?></p> <form class="delete-form" method="POST" onsubmit="return confirm('Are you sure you want to delete this image?');"> <input type="hidden" name="filename" value="<?php echo htmlspecialchars($image); ?>"> <input type="hidden" name="page" value="<?php echo $currentPage; ?>"> <button type="submit" name="delete_image">Hapus</button> </form> </div> <?php } ?> </div> <hr> <?php if ($totalPages > 1): ?> <div class="pagination"> <?php if ($currentPage > 1): ?> <a href="?page=<?php echo $currentPage - 1; ?>">Prev</a> <?php else: ?> <span style="opacity: 0.5;">Prev</span> <?php endif; ?> <?php $range = 2; $start = max(1, $currentPage - $range); $end = min($totalPages, $currentPage + $range); if ($start > 1) { echo '...'; } for ($i = $start; $i <= $end; $i++): ?> <?php if ($i == $currentPage): ?> <span class="current-page"><?php echo $i; ?></span> <?php else: ?> <a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a> <?php endif; ?> <?php endfor; ?> <?php if ($end < $totalPages) { echo '...'; } if ($currentPage < $totalPages): ?> <a href="?page=<?php echo $currentPage + 1; ?>">Next</a> <?php else: ?> <span style="opacity: 0.5;">Next</span> <?php endif; ?> </div> <?php endif; ?> <p style="color: #9e9e9e;">Total Images: <strong><?php echo $totalImages; ?></strong> | Total Pages: <strong><?php echo $totalPages; ?></strong></p> <div id="myModal" class="modal"> <span class="close" onclick="closeModal()">×</span> <img class="modal-content" id="img01"> <div id="caption"></div> <a class="prev" onclick="plusSlides(-1)">❮</a> <a class="next" onclick="plusSlides(1)">❯</a> </div> <script> // FUNGSI KONFIRMASI YANG DIMODIFIKASI function confirmUpload() { // Cek apakah ada file yang dipilih const fileInput = document.querySelector('input[name="image"]'); if (fileInput.files.length === 0) { alert("Please select a file to upload."); return false; } else if (fileInput.files[0].size > 5 * 1024 * 1024) { alert("File size exceeds the <?php echo $maxSize / (1024 * 1024); ?> MB limit."); return false; } const fileName = fileInput.files[0].name; const fileSizeMB = (fileInput.files[0].size / (1024 * 1024)).toFixed(2); const confirmationMessage = ` Are you sure you want to upload and process this image? File: ${fileName} Size: ${fileSizeMB} MB / Max: <?php echo $maxSize / (1024 * 1024); ?> MB Note: Image will be resized to <?php echo $targetWidth; ?>x<?php echo $targetHeight; ?> and a thumbnail will be created. `; if (confirm(confirmationMessage)) { // --- FUNGSI BARU: Tampilkan Overlay setelah konfirmasi berhasil --- document.getElementById('loading-overlay').style.display = 'flex'; return true; // Lanjutkan submit form } return false; // Batalkan submit form } // FUNGSI GALERI DAN MODE GELAP (Kode asli Anda) const allImageUrls = <?php echo json_encode($allImageUrls); ?>; let slideIndex; const modal = document.getElementById("myModal"); const modalImg = document.getElementById("img01"); const captionText = document.getElementById("caption"); function openModal(element) { // Pastikan modal tidak memiliki kelas closing (dari penutupan sebelumnya) modal.classList.remove('closing'); modal.style.display = "block"; const clickedUrl = element.getAttribute('data-fullsize-url'); slideIndex = allImageUrls.indexOf(clickedUrl); showSlides(slideIndex); } // --- MODIFIKASI: FUNGSI close Modal ditambahkan efek zoom-out --- function closeModal() { // 1. Tambahkan kelas 'closing' untuk memicu CSS transition/animation zoom-out modal.classList.add('closing'); // 2. Gunakan setTimeout untuk benar-benar menyembunyikan modal setelah transisi selesai // Durasi timeout harus sesuai dengan durasi CSS transition (0.5s) setTimeout(() => { modal.style.display = "none"; modalImg.src = ""; // Bersihkan src // Opsional: Hapus kelas 'closing' agar modal siap untuk pembukaan berikutnya modal.classList.remove('closing'); }, 500); // 500ms = 0.5 detik } // ------------------------------------------------------------------- function plusSlides(n) { slideIndex += n; if (slideIndex >= allImageUrls.length) slideIndex = 0; if (slideIndex < 0) slideIndex = allImageUrls.length - 1; // --- MODIFIKASI: Panggil showSlides dengan efek fade --- showSlides(slideIndex, true); } // --- MODIFIKASI: Fungsi showSlides ditambahkan parameter useFade --- function showSlides(n, useFade = false) { if (allImageUrls.length === 0) return; const currentUrl = allImageUrls[n]; if (useFade) { // 1. Tambahkan kelas fade-out (opacity: 0) untuk efek pudar keluar modalImg.classList.add('fade-out'); // 2. Tunggu sebentar (sesuai durasi transisi CSS) sebelum ganti gambar setTimeout(() => { modalImg.src = currentUrl; // 3. Setelah src diganti dan gambar mulai loading, hapus kelas fade-out // Ini akan memicu fade-in (opacity: 1) modalImg.classList.remove('fade-out'); }, 300); // Durasi ini harus sama dengan transition CSS (0.3s) } else { // Untuk openModal pertama kali (tanpa fade) modalImg.src = currentUrl; } // Update caption (tidak perlu menunggu fade) const urlObj = new URL(currentUrl, window.location.origin); const fileName = urlObj.searchParams.get("file"); captionText.innerHTML = fileName ? fileName : "Image"; } // ------------------------------------------------------------------- document.addEventListener('keydown', function (e) { if (modal.style.display === "block") { if (e.key === "ArrowRight") plusSlides(1); else if (e.key === "ArrowLeft") plusSlides(-1); else if (e.key === "Escape") closeModal(); } }); modal.addEventListener('click', function(e) { // Tutup modal jika mengklik latar belakang, // tetapi pastikan modal sudah selesai animasinya (tidak memiliki kelas 'closing') if (e.target === modal && !modal.classList.contains('closing')) { closeModal(); } }); // --- Dark Mode Script --- const themeToggle = document.getElementById('theme-toggle'); const body = document.body; const darkModeKey = 'darkModeEnabled'; const iconSpan = themeToggle.querySelector('.icon'); const textSpan = themeToggle.querySelector('.text'); function setDarkMode(isEnabled) { if (isEnabled) { body.classList.add('dark-mode'); body.classList.remove('light-mode'); iconSpan.textContent = '🌙'; textSpan.textContent = 'Dark Mode'; localStorage.setItem(darkModeKey, 'true'); } else { body.classList.remove('dark-mode'); body.classList.add('light-mode'); iconSpan.textContent = '☀️'; textSpan.textContent = 'Light Mode'; localStorage.setItem(darkModeKey, 'false'); } } const savedMode = localStorage.getItem(darkModeKey); let initialDarkMode = false; if (savedMode !== null) initialDarkMode = savedMode === 'true'; else initialDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; setDarkMode(initialDarkMode); themeToggle.addEventListener('click', () => { setDarkMode(!body.classList.contains('dark-mode')); }); </script> </body> </html>
Run Code
<?php echo "Hello from PHP!"; ?>
Run Code New Tab
Result