Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Image Sequence Album β Video Manner</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
background: #0a0c10;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
margin: 0;
padding: 20px;
}
.album-container {
background: #1e1f24;
border-radius: 32px;
box-shadow: 0 20px 40px rgba(0,0,0,0.5);
padding: 20px;
max-width: 1000px;
width: 100%;
}
.viewer {
background: #000;
border-radius: 24px;
overflow: hidden;
aspect-ratio: 4 / 3; /* good for most photos, adjust if needed */
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.viewer img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
display: block;
transition: opacity 0.2s ease;
background: #000;
}
.controls {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
align-items: center;
}
button {
background: #2c2f36;
border: none;
color: white;
font-size: 1.2rem;
padding: 8px 18px;
border-radius: 60px;
cursor: pointer;
font-weight: 600;
transition: 0.2s;
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
}
button:hover {
background: #3d424c;
transform: scale(0.97);
}
button:active {
background: #1a1c22;
}
.speed-slider {
display: flex;
align-items: center;
gap: 10px;
background: #2c2f36;
padding: 5px 15px;
border-radius: 40px;
color: white;
}
input {
cursor: pointer;
}
.status {
background: #000000aa;
backdrop-filter: blur(8px);
color: #ddd;
font-size: 0.85rem;
padding: 4px 12px;
border-radius: 30px;
font-family: monospace;
}
.progress-bar {
flex: 1;
min-width: 150px;
height: 6px;
background: #3a3f4b;
border-radius: 10px;
overflow: hidden;
}
.progress-fill {
width: 0%;
height: 100%;
background: #4c9aff;
transition: width 0.1s linear;
}
.filename-badge {
background: #00000099;
position: absolute;
bottom: 12px;
right: 12px;
color: white;
font-size: 12px;
padding: 4px 10px;
border-radius: 20px;
font-family: monospace;
backdrop-filter: blur(4px);
pointer-events: none;
}
h3 {
text-align: center;
margin: 0 0 12px 0;
font-weight: 500;
color: #ccc;
font-size: 1rem;
}
.note {
font-size: 12px;
text-align: center;
margin-top: 16px;
color: #6c6f78;
}
@media (max-width: 640px) {
.controls { gap: 8px; }
button { padding: 6px 12px; font-size: 1rem; }
}
</style>
</head>
<body>
<div class="album-container">
<h3>ποΈ ALBUM BOOK Β· VIDEO MANNER</h3>
<div class="viewer">
<img id="slideshow-img" alt="slideshow frame">
<div class="filename-badge" id="filename-badge">loading...</div>
</div>
<div class="controls">
<button id="playBtn">βΆ PLAY</button>
<button id="pauseBtn">βΈ PAUSE</button>
<button id="prevBtn">β PREV</button>
<button id="nextBtn">NEXT βΆ</button>
<div class="speed-slider">
<span>π’</span>
<input type="range" id="speedControl" min="0.5" max="4" step="0.1" value="1">
<span>π</span>
<span id="speedValue" style="min-width: 45px;">1.0x</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="status" id="counterStatus">0 / 0</div>
</div>
<div class="note">
β
Corrected sequence (duplicates removed): 10β15, 18β23, 25β34, 36β42, 45β46, 48β50<br>
π Images must be in same folder as this HTML, named like <strong>10.png</strong>, <strong>11.png</strong> ... (supports .jpg/.jpeg/.png)
</div>
</div>
<script>
// --------------------------------------------------------------
// FIXED SEQUENCE β duplicates and mistakes removed
// (original had two 42, two 45 β fixed)
// --------------------------------------------------------------
const imageNumbers = [
10, 11, 12, 13, 14, 15,
18, 19, 20, 21, 22, 23,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
36, 37, 38, 39, 40, 41, 42,
45, 46,
48, 49, 50
];
// Try common extensions β browser will load first that exists.
// We'll attempt .png first, then .jpg, then .jpeg (simple fallback)
// For reliability, we auto-detect via onerror.
const extensions = ['png', 'jpg', 'jpeg', 'PNG', 'JPG', 'JPEG'];
let currentIndex = 0;
let timer = null;
let isPlaying = true;
let slideDurationSec = 1.5; // default seconds per image, speed multiplier applied
let speedMultiplier = 1.0; // from slider (1x = 1.5 sec, 2x = 0.75 sec)
const imgElement = document.getElementById('slideshow-img');
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const speedControl = document.getElementById('speedControl');
const speedValueSpan = document.getElementById('speedValue');
const counterStatus = document.getElementById('counterStatus');
const progressFill = document.getElementById('progressFill');
const filenameBadge = document.getElementById('filename-badge');
let totalImages = imageNumbers.length;
// Helper: get current effective delay (seconds)
function getEffectiveDelay() {
return slideDurationSec / speedMultiplier;
}
// Update speed display and restart timer if playing
function updateSpeed() {
speedMultiplier = parseFloat(speedControl.value);
speedValueSpan.innerText = speedMultiplier.toFixed(1) + 'x';
if (isPlaying) {
restartTimer();
}
}
// Clear running timer
function stopTimer() {
if (timer) {
clearTimeout(timer);
timer = null;
}
}
// Restart timer based on current image and playing state
function restartTimer() {
stopTimer();
if (isPlaying) {
timer = setTimeout(showNextImage, getEffectiveDelay() * 1000);
}
}
// Update UI: image, counter, progress bar, badge
function loadImageByIndex(index) {
if (index < 0) index = 0;
if (index >= totalImages) {
// Loop to beginning
if (totalImages === 0) return;
index = 0;
currentIndex = 0;
}
currentIndex = index;
const imgNum = imageNumbers[currentIndex];
// Show loading state
imgElement.style.opacity = '0.5';
const baseName = imgNum.toString();
// Attempt to load with first found extension
tryLoadImage(baseName, 0);
// Update text info
counterStatus.innerText = `${currentIndex+1} / ${totalImages}`;
updateProgressBar();
filenameBadge.innerText = `${baseName}.???`;
// Restart timer only if playing (after load, but restart anyway when called from next/prev)
if (isPlaying) {
restartTimer();
}
}
function tryLoadImage(baseName, extIndex) {
if (extIndex >= extensions.length) {
// fallback: try raw name without extension? not needed.
imgElement.src = '';
imgElement.alt = 'missing image';
imgElement.style.opacity = '1';
filenameBadge.innerText = `${baseName} (not found)`;
return;
}
const ext = extensions[extIndex];
const srcPath = `${baseName}.${ext}`;
const testImg = new Image();
testImg.onload = () => {
imgElement.src = srcPath;
imgElement.alt = `frame ${baseName}`;
imgElement.style.opacity = '1';
filenameBadge.innerText = `${baseName}.${ext}`;
};
testImg.onerror = () => {
// try next extension
tryLoadImage(baseName, extIndex + 1);
};
testImg.src = srcPath;
}
function updateProgressBar() {
if (totalImages === 0) return;
const percent = ((currentIndex) / (totalImages - 1)) * 100;
progressFill.style.width = `${percent}%`;
}
function showNextImage() {
if (totalImages === 0) return;
let next = currentIndex + 1;
if (next >= totalImages) {
// loop: go back to start
next = 0;
}
loadImageByIndex(next);
}
function showPrevImage() {
if (totalImages === 0) return;
let prev = currentIndex - 1;
if (prev < 0) prev = totalImages - 1;
loadImageByIndex(prev);
}
// play/pause logic
function playSequence() {
if (!isPlaying) {
isPlaying = true;
restartTimer();
}
}
function pauseSequence() {
if (isPlaying) {
isPlaying = false;
stopTimer();
}
}
// event binding
playBtn.addEventListener('click', playSequence);
pauseBtn.addEventListener('click', pauseSequence);
prevBtn.addEventListener('click', () => {
pauseSequence(); // optional: keep paused after manual navigation
showPrevImage();
});
nextBtn.addEventListener('click', () => {
pauseSequence();
showNextImage();
});
speedControl.addEventListener('input', () => {
updateSpeed();
});
// optional: allow user to change base duration? default 1.5 sec per image (nice speed)
// we can hardcode but also make it tweakable: extend?
// For better experience, add small key shortcut: left/right arrows
window.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
e.preventDefault();
pauseSequence();
showPrevImage();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
pauseSequence();
showNextImage();
} else if (e.key === ' ' || e.key === 'Space') {
e.preventDefault();
if (isPlaying) pauseSequence();
else playSequence();
}
});
// initial load
if (totalImages > 0) {
loadImageByIndex(0);
// start playing immediately
isPlaying = true;
restartTimer();
} else {
counterStatus.innerText = '0 / 0';
filenameBadge.innerText = 'no images';
}
// set slideDurationSec default (1.5 seconds per image feels like smooth video)
slideDurationSec = 1.5;
// sync speed display
speedControl.value = "1.0";
updateSpeed();
</script>
</body>
</html>. html 7
1
12KB
14KB
192.0ms
224.0ms
307.0ms