photoprism-slideshow/index.html

403 lines
14 KiB
HTML
Raw Normal View History

2023-02-19 10:28:11 +00:00
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="theme-color" content="#ffffff" />
<script>
const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)");
document.querySelector('head meta[name=theme-color]').content = darkModePreference ? '#000' : '#fff';
</script>
<style>
:root {
--foreground: #000;
--background: #fff;
--border-light: #999;
--border-dark: #000;
}
.dark {
--foreground: #fff;
--background: #000;
--border-dark: #999;
--border-light: #fff;
}
body {
overflow-x: hidden;
max-width: 100%;
margin: 0;
background-color: var(--background);
color: var(--foreground);
}
button {
border-top: 2px solid var(--border-light);
border-left: 2px solid var(--border-light);
border-right: 2px solid var(--border-dark);
border-bottom: 2px solid var(--border-dark);
background-color: var(--background);
color: var(--foreground);
transition: transform 0.5s ease-in-out;
cursor: grab;
}
button:hover {
transform: scale(1.1);
}
form {
display: grid;
grid-template-columns: auto auto 1fr;
justify-items: left;
}
form label {
grid-column: 1/2;
}
form input {
grid-column: 2/3;
}
#headline {
grid-column: 1/4;
}
fieldset {
grid-row: 6;
}
form button[name="submit"] {
grid-column: 1;
grid-row: 7;
margin-top: 8px;
}
input[type="number"] {
max-width: 50px;
}
img {
max-width: 100%;
width: 100%;
max-height: 100%;
object-fit: contain;
transition-property: translate;
transition-timing-function: ease-in-out;
position: absolute;
}
.fast {
transition-duration: 0.5s;
}
.normal {
transition-duration: 1s;
}
.slow {
transition-duration: 2s;
}
img.instant {
transition-duration: 0s;
transition-delay: 0s;
}
.right {
translate: 200%;
}
.left {
translate: -200%;
}
.hidden {
display: none;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
const controls = document.getElementById('controls');
initControls(controls);
const firstImage = document.getElementById('first');
const secondImage = document.getElementById('second');
const marginInput = document.getElementById('margin');
window.showTimeSeconds = 1;
window.nextPhotoOffset = 0;
window.shuffle = false;
window.photoCount = 2;
window.displayed = [];
loadNextPhoto(firstImage);
loadNextPhoto(secondImage);
marginInput.addEventListener('change', (event) => {
updateMargins(event.target.value);
});
updateMargins(marginInput.value);
document.addEventListener("keyup", (event) => {
if (event.isComposing || event.keyCode === 229) {
return;
}
if (controls.elements['submit'].value === 'go') {
return;
}
switch (event.key) {
case 's':
controls.classList.toggle('hidden');
break;
case ' ':
if (window.isPaused) {
const first = document.getElementById('first');
const second = document.getElementById('second');
first.style.transitionDelay = window.showTimeSeconds + 's';
second.style.transitionDelay = window.showTimeSeconds + 's';
let leftElement, rightElement;
if (first.className.includes('right')) {
leftElement = second;
rightElement = first;
} else {
leftElement = first;
rightElement = second;
}
leftElement.classList.add('left');
rightElement.classList.remove('right');
window.isPaused = false;
} else {
const { left: leftElement, right: rightElement} = getImagePositions();
leftElement.style.transitionDelay = '0s';
rightElement.style.transitionDelay = '0s';
leftElement.classList.remove('left');
rightElement.classList.add('right');
window.isPaused = true;
}
break;
case 'ArrowRight':
const { left: leftElement, right: rightElement} = getImagePositions();
leftElement.classList.remove('left');
rightElement.classList.add('right');
first.style.transitionDelay = '0s';
second.style.transitionDelay = '0s';
setTimeout(() => {
leftElement.classList.add('left');
rightElement.classList.remove('right');
setTimeout(() => {
first.style.transitionDelay = window.showTimeSeconds + 's';
second.style.transitionDelay = window.showTimeSeconds + 's';
});
});
break;
case 'r': {
window.shuffle = !window.shuffle;
if (!window.shuffle) {
window.nextPhotoOffset;
}
const searchParams = new URLSearchParams(window.location.search);
searchParams.set('random', window.shuffle);
history.pushState(
null,
'',
window.location.pathname + '?' + searchParams.toString()
);
document.getElementById('controls').elements['random'].checked = window.shuffle;
}
}
});
document.querySelectorAll('input[name=theme]')
.forEach(i => i.addEventListener('change', (e) => setDarkMode(e.target.value === 'dark')));
controls.addEventListener('submit', (event) => {
event.preventDefault();
const data = new FormData(event.target);
window.showTimeSeconds = parseInt(data.get('showTime'));
window.shuffle = data.get('random') === 'true';
const margin = parseInt(data.get('margin'));
const speed = data.get('speed');
const theme = data.get('theme');
window.nextPhotoOffset--;
loadNextPhoto(secondImage);
document.querySelectorAll('img').forEach(i => {
i.classList.remove('normal');
i.classList.remove('slow');
i.classList.remove('fast');
i.classList.add(speed);
});
const searchParams = new URLSearchParams(window.location.search);
searchParams.set('showTime', window.showTimeSeconds);
searchParams.set('random', window.shuffle);
searchParams.set('margin', margin);
searchParams.set('speed', speed);
searchParams.set('theme', theme);
history.pushState(
null,
'',
window.location.pathname + '?' + searchParams.toString()
);
controls.classList.add('hidden');
document.getElementById('first').style.transitionDelay = window.showTimeSeconds + 's';
document.getElementById('second').style.transitionDelay = window.showTimeSeconds + 's';
if (controls.elements['submit'].value === 'save') {
return;
}
firstImage.classList.remove('hidden');
secondImage.classList.remove('hidden');
setTimeout(() => {
document.getElementById('first').classList.add('left');
document.getElementById('second').classList.remove('right');
});
controls.elements['submit'].value = 'save';
controls.elements['submit'].innerText = 'Speichern';
});
let remainingTransitions = document.querySelectorAll('img').length;
addEventListener('transitionend', (event) => {
if (event.target.tagName !== 'IMG') {
return;
}
remainingTransitions--;
if (remainingTransitions > 0) {
return;
}
remainingTransitions = document.querySelectorAll('img').length;
const leftElement = document.querySelector('.left');
const rightElement = document.querySelector('img:not([class~=left])');
leftElement.classList.add('instant');
leftElement.style.transitionDelay = '0s';
// Wait until 'instant' and transitionDelay are applied
setTimeout(() => {
leftElement.classList.remove('left');
leftElement.classList.add('right');
// Wait until element is moved
setTimeout(() => {
leftElement.classList.remove('instant');
leftElement.style.transitionDelay = window.showTimeSeconds + 's';
// Wait until 'instant' is removed and transitionDelay applied
setTimeout(() => {
leftElement.classList.remove('right');
rightElement.classList.add('left');
loadNextPhoto(leftElement);
});
});
});
});
});
function loadNextPhoto(element, overwriteOffset) {
let offset = window.nextPhotoOffset;
if (window.shuffle) {
if (window.displayed.length >= window.photoCount * 0.75) {
window.displayed = [];
}
let count = Math.floor(window.photoCount * 0.5);
do {
offset = Math.floor(Math.random() * (window.photoCount + 1));
count--;
} while (count > 0 && window.displayed.includes(offset));
}
if (overwriteOffset !== undefined) {
offset = overwriteOffset
}
const album = (new URLSearchParams(window.location.search)).get('album');
fetch(`photos.php?album=${album}&offset=${offset}`)
.then((response) => response.json())
.then((data) => {
if (data['error']) {
console.error('Error fetching photo', data['error']);
} else {
document.getElementById('headline').innerText = `Zeige Album: ${data.album}`;
element.src = data.src;
window.photoCount = data.count;
window.displayed.push(offset);
}
})
.catch(error => {
console.error('Error fetching photo', error);
});
window.nextPhotoOffset = offset + 1;
}
function getImagePositions() {
const first = document.getElementById('first');
const second = document.getElementById('second');
let leftElement, rightElement;
if (first.className.includes('left')) {
leftElement = first;
rightElement = second;
} else {
leftElement = second;
rightElement = first;
}
return {
left: leftElement,
right: rightElement
};
}
function updateMargins(margin) {
document.querySelectorAll('img').forEach(i => {
i.style.margin = margin + 'px';
i.style.width = `calc(100% - ${2*margin}px)`;
i.style.maxHeight = `calc(100% - ${2*margin}px)`;
i.style.maxWidth = `calc(100% - ${2*margin}px)`;
});
controls.style.margin = margin + 'px';
}
function initControls(form) {
const initialParams = new URLSearchParams(window.location.search);
for (const [key, val] of initialParams.entries()) {
const input = form.elements[key];
if (input === undefined) {
continue;
}
switch (input.type) {
case 'checkbox':
input.checked = val === 'true';
break;
default:
input.value = val;
break;
}
}
let theme =initialParams.get('theme');
if (theme === undefined || theme === null) {
const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)");
theme = darkModePreference ? 'dark' : 'light';
}
form.elements['theme'].value = theme;
setDarkMode(theme === 'dark');
}
function setDarkMode(mode) {
if (mode) {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
}
</script>
</head>
<body>
<form id="controls">
<h3 id="headline">Zeige Album: </h3>
<label for="random">Zufällig</label>
<input type="checkbox" name="random" id="random" value="true"/>
<label for="showTime">Zeit zwischen Bildern (Sekunden)</label>
<input type="number" name="showTime" id="showTime" value="5" required min=1/>
<label for="margin">Seitenrand</label>
<input type="number" name="margin" id="margin" value="30" required min=0/>
<label for="margin">Animationsgeschwindigkeit</label>
<select name="speed" id="speed">
<option value="slow">Langsam</option>
<option value="normal" selected>Normal</option>
<option value="fast">Schnell</option>
</select>
<fieldset>
<legend>Hintergrundfarbe</legend>
<label for="lightRadio">Hell</label>
<input id="lightRadio" type="radio" name="theme" value="light" />
<label for="darkRadio">Dunkel</label>
<input id="darkRadio" type="radio" name="theme" value="dark" />
</fieldset>
<button type="submit" name="submit" value="go">Los</button>
</form>
<img src="" id="first" class="hidden normal" />
<img src="" id="second" class="hidden right normal" />
</body>
</html>