mirror of
https://github.com/immich-app/immich.git
synced 2025-12-21 09:15:44 +03:00
The current implementation mixes intervals and animation frames, which is a little convoluted. The use of intervals means that the animation is not going to be smooth and may have strange behaviour when the window is moved to the background. It's possible that the current animation frames could pile up and run all at once which would be undesirable. Moving everything into animation frames means the code is simpler and easier to reason about. It should also be more performant and less buggy. Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
96 lines
2.7 KiB
Svelte
96 lines
2.7 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { DateTime } from 'luxon';
|
|
import { MemoryLaneResponseDto, api } from '@api';
|
|
import ChevronLeft from 'svelte-material-icons/ChevronLeft.svelte';
|
|
import ChevronRight from 'svelte-material-icons/ChevronRight.svelte';
|
|
import { memoryStore } from '$lib/stores/memory.store';
|
|
import { goto } from '$app/navigation';
|
|
|
|
let memoryLane: MemoryLaneResponseDto[] = [];
|
|
$: shouldRender = memoryLane.length > 0;
|
|
|
|
onMount(async () => {
|
|
const { data } = await api.assetApi.getMemoryLane({
|
|
timestamp: DateTime.local().startOf('day').toISO()
|
|
});
|
|
|
|
memoryLane = data;
|
|
$memoryStore = data;
|
|
});
|
|
|
|
let memoryLaneElement: HTMLElement;
|
|
let offsetWidth = 0;
|
|
let innerWidth = 0;
|
|
$: isOverflow = offsetWidth < innerWidth;
|
|
|
|
function scrollLeft() {
|
|
memoryLaneElement.scrollTo({
|
|
left: memoryLaneElement.scrollLeft - 400,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
|
|
function scrollRight() {
|
|
memoryLaneElement.scrollTo({
|
|
left: memoryLaneElement.scrollLeft + 400,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
</script>
|
|
|
|
{#if shouldRender}
|
|
<section
|
|
id="memory-lane"
|
|
bind:this={memoryLaneElement}
|
|
class="relative overflow-x-hidden whitespace-nowrap mt-5 transition-all"
|
|
bind:offsetWidth
|
|
>
|
|
{#if isOverflow}
|
|
<div class="sticky left-0 z-20">
|
|
<div class="absolute right-0 top-[6rem] z-20">
|
|
<button
|
|
class="rounded-full opacity-50 hover:opacity-100 p-2 border border-gray-500 bg-gray-100 text-gray-500"
|
|
on:click={scrollRight}
|
|
>
|
|
<ChevronRight size="36" /></button
|
|
>
|
|
</div>
|
|
|
|
<div class="absolute left-0 top-[6rem] z-20">
|
|
<button
|
|
class="rounded-full opacity-50 hover:opacity-100 p-2 border border-gray-500 bg-gray-100 text-gray-500"
|
|
on:click={scrollLeft}><ChevronLeft size="36" /></button
|
|
>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="inline-block" bind:offsetWidth={innerWidth}>
|
|
{#each memoryLane as memory, i (memory.title)}
|
|
<button
|
|
class="memory-card relative inline-block mr-8 rounded-xl aspect-video h-[215px]"
|
|
on:click={() => goto(`/memory?memory=${i}`)}
|
|
>
|
|
<img
|
|
class="rounded-xl h-full w-full object-cover"
|
|
src={api.getAssetThumbnailUrl(memory.assets[0].id, 'JPEG')}
|
|
alt={memory.title}
|
|
draggable="false"
|
|
/>
|
|
<p class="absolute bottom-2 left-4 text-lg text-white z-10">{memory.title}</p>
|
|
<div
|
|
class="absolute top-0 left-0 w-full h-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent z-0 hover:bg-black/20 transition-all"
|
|
/>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
|
|
<style>
|
|
.memory-card {
|
|
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px;
|
|
}
|
|
</style>
|