A fullscreen auto-advancing slideshow any user can start. Design:
docs/CONCEPT_DIASHOW.md.
- lib/diashow/queue.ts: SlideQueue state machine — liveQueue drains first
(FIFO, seeded by SSE upload-processed), then shuffleQueue (refilled
from allKnown minus a 5-id ring buffer of recently shown). Pure logic,
unit-testable.
- lib/diashow/wakelock.ts: Screen Wake Lock wrapper that re-acquires on
visibility change (the OS drops the lock when the tab hides).
- lib/diashow/transitions/{index,crossfade,kenburns}.ts: registry +
the v1 transitions. Adding a new animation is one file + one entry —
the extensibility target from docs/FEATURES §2.9.
- routes/diashow/+page.svelte: fullscreen page, hides bottom nav,
6 s default dwell (3/6/10 configurable), keyboard shortcuts
(Escape exits, Space toggles pause), tap-to-reveal overlay with
pause / dwell / transition / exit. Respects $dataMode to choose
preview vs. original URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
1.2 KiB
Svelte
52 lines
1.2 KiB
Svelte
<script lang="ts">
|
|
// Crossfade + slow zoom and pan (the "Ken Burns" effect). Image-only — videos
|
|
// fall back to the crossfade behaviour to avoid double-animation.
|
|
|
|
interface Props {
|
|
src: string;
|
|
isVideo: boolean;
|
|
durationMs: number;
|
|
}
|
|
|
|
let { src, isVideo, durationMs }: Props = $props();
|
|
|
|
// Mild random pan so each slide feels different. Range chosen so the image never
|
|
// pans out of frame given the object-fit: cover.
|
|
const panX = Math.round((Math.random() - 0.5) * 6); // -3% .. +3%
|
|
const panY = Math.round((Math.random() - 0.5) * 6);
|
|
</script>
|
|
|
|
<div
|
|
class="absolute inset-0 flex items-center justify-center overflow-hidden bg-black"
|
|
style="animation: kb-fade {durationMs}ms ease-out forwards;"
|
|
>
|
|
{#if isVideo}
|
|
<!-- svelte-ignore a11y_media_has_caption -->
|
|
<video
|
|
{src}
|
|
autoplay
|
|
muted
|
|
playsinline
|
|
class="h-full w-full object-contain"
|
|
></video>
|
|
{:else}
|
|
<img
|
|
{src}
|
|
alt=""
|
|
class="h-full w-full object-cover"
|
|
style="animation: kb-zoom 10s ease-out forwards; transform-origin: {50 + panX}% {50 + panY}%;"
|
|
/>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
@keyframes kb-fade {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
@keyframes kb-zoom {
|
|
from { transform: scale(1.0); }
|
|
to { transform: scale(1.1); }
|
|
}
|
|
</style>
|