Migrate slideshow to alpine

This commit is contained in:
2025-09-22 13:26:28 +02:00
parent 31aee01360
commit dff23fae7f
4 changed files with 106 additions and 151 deletions

View File

@@ -0,0 +1,47 @@
const INTERVAL = 10;
interface Poster {
url: string; // URL of the poster
displayTime: number; // Number of seconds to display that poster
}
document.addEventListener("alpine:init", () => {
Alpine.data("slideshow", (posters: Poster[]) => ({
posters: posters,
progress: 0,
elapsed: 0,
current: 0,
init() {
this.$watch("elapsed", () => {
const displayTime = this.posters[this.current].displayTime * 1000;
if (this.elapsed > displayTime) {
this.current = this.getNext();
this.elapsed = 0;
}
if (displayTime === 0) {
this.progress = 100;
} else {
this.progress = (100 * this.elapsed) / displayTime;
}
});
setInterval(() => {
this.elapsed += INTERVAL;
}, INTERVAL);
},
getNext() {
return (this.current + 1) % this.posters.length;
},
async toggleFullScreen(event: Event) {
const target = event.target as HTMLElement;
if (document.fullscreenElement) {
await document.exitFullscreen();
return;
}
await target.requestFullscreen();
},
}));
});

View File

@@ -1,98 +0,0 @@
$(document).ready(() => {
const transitionTime = 1000;
let i = 0;
const max = $("#slideshow .slide").length;
function enterFullscreen() {
const element = document.getElementById("slideshow");
$(element).addClass("fullscreen");
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}
function exitFullscreen() {
const element = document.getElementById("slideshow");
$(element).removeClass("fullscreen");
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
function initProgressBar() {
$("#slideshow #progress_bar").css("transition", "none");
$("#slideshow #progress_bar").removeClass("progress");
$("#slideshow #progress_bar").addClass("init");
}
function startProgressBar(displayTime) {
$("#slideshow #progress_bar").removeClass("init");
$("#slideshow #progress_bar").addClass("progress");
$("#slideshow #progress_bar").css("transition", `width ${displayTime}s linear`);
}
function next() {
initProgressBar();
const slide = $($("#slideshow .slide").get(i % max));
slide.removeClass("center");
slide.addClass("left");
const nextSlide = $($("#slideshow .slide").get((i + 1) % max));
nextSlide.removeClass("right");
nextSlide.addClass("center");
const displayTime = nextSlide.attr("display_time") || 2;
$("#slideshow .bullet").removeClass("active");
const bullet = $("#slideshow .bullet")[(i + 1) % max];
$(bullet).addClass("active");
i = (i + 1) % max;
setTimeout(() => {
const othersLeft = $("#slideshow .slide.left");
othersLeft.removeClass("left");
othersLeft.addClass("right");
startProgressBar(displayTime);
setTimeout(next, displayTime * 1000);
}, transitionTime);
}
const displayTime = $("#slideshow .center").attr("display_time");
initProgressBar();
setTimeout(() => {
if (max > 1) {
startProgressBar(displayTime);
setTimeout(next, displayTime * 1000);
}
}, 10);
$("#slideshow").click(() => {
if ($("#slideshow").hasClass("fullscreen")) {
exitFullscreen();
} else {
enterFullscreen();
}
});
$(document).keyup((e) => {
if (e.keyCode === 27) {
// escape key maps to keycode `27`
e.preventDefault();
exitFullscreen();
}
});
});

View File

@@ -1,4 +1,4 @@
body{
body {
position: absolute;
width: 100vw;
height: 100vh;
@@ -7,22 +7,22 @@ body{
margin: 0;
}
#slideshow{
#slideshow {
position: relative;
background-color: lightgrey;
height: 100%;
*{
* {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
&:hover{
&:hover {
&::before{
&::before {
position: absolute;
width: 100%;
@@ -43,7 +43,7 @@ body{
}
&.fullscreen{
&:fullscreen {
position: fixed;
width: 100%;
height: 100%;
@@ -51,57 +51,58 @@ body{
left: 0;
background: none;
&:before{
display:none;
&:before {
display: none;
}
#slides{
#slides {
height: 100vh;
}
}
#slides{
#slides {
position: relative;
height: 100%;
overflow: hidden;
background-color: grey;
.slide{
.slide {
position: absolute;
width: 100%;
height: 100%;
display: inline-flex;
display: none;
justify-content: center;
top: 0px;
background-color: grey;
transition: left 1s ease-out;
img{
img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
&.active {
animation: scrolling 1s;
display: inline-flex;
}
.slide.left{
left: -100%;
@keyframes scrolling {
0% {
transform: translateX(100%);
}
.slide.center{
left: 0px;
100% {
transform: translateX(0%);
}
}
.slide.right{
left: 100%;
transition: none;
}
}
#progress_bullets{
#progress_bullets {
position: absolute;
bottom: 10px;
width: 100%;
@@ -112,7 +113,7 @@ body{
margin-bottom: 10px;
.bullet{
.bullet {
height: 10px;
width: 10px;
@@ -123,27 +124,23 @@ body{
background-color: grey;
&.active{
&.active {
background-color: #c99836;
}
}
}
#progress_bar{
progress {
position: absolute;
bottom: 0px;
height: 10px;
background-color: #304c83;
&.init{
width: 0px;
transition: none;
}
&.progress{
// color: #304c83;
width: 100%;
transition: width 10s linear;
margin-bottom: 0px;
border: none;
&[value] {
background-color: transparent;
}
}
}

View File

@@ -3,27 +3,36 @@
<head>
<title>{% trans %}Slideshow{% endtrans %}</title>
<link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" />
<script src="{{ static('bundled/vendored/jquery.min.js') }}"></script>
<script src="{{ static('com/js/slideshow.js') }}"></script>
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
<script type="module" src="{{ static('bundled/com/slideshow-index.ts') }}"></script>
</head>
<body>
<div id="slideshow">
<body x-data="slideshow([
{% for poster in posters %}
{
url: '{{ poster.file.url }}',
displayTime: {{ poster.display_time }}
},
{% endfor %}
])">
<div id="slideshow" @click="toggleFullScreen">
<div id="slides">
{% for poster in posters %}
<div class="slide {% if loop.first %}center{% else %}right{% endif %}" display_time="{{ poster.display_time }}">
<img src="{{ poster.file.url }}">
<template x-for="(poster, index) in posters">
<div class="slide" :class="{
active: index === current,
}">
<img :src="poster.url">
</div>
{% endfor %}
</template>
</div>
<div id="progress_bullets">
{% for poster in posters %}
<div class="bullet {% if loop.first %}active{% endif %}"></div>
{% endfor %}
<template x-for="(poster, index) in posters">
<div class="bullet" :class="{active: current === index}"></div>
</template>
</div>
<div id="progress_bar"></div>
<progress :value="progress" max="100" x-show="posters.length > 1 && progress > 0"></progress>
</div>
</body>