add zoom to image carousel
All checks were successful
deploy / build-and-deploy (push) Successful in 14s
All checks were successful
deploy / build-and-deploy (push) Successful in 14s
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cat1 from '@assets/img/cat1.jpg';
|
import cat1 from '@assets/img/cat1.jpg';
|
||||||
import cat2 from '@assets/img/cat2.jpg';
|
import cat2 from '@assets/img/cat2.jpg';
|
||||||
import { untrack } from 'svelte';
|
import { tick, untrack } from 'svelte';
|
||||||
|
|
||||||
// html bindings
|
// html bindings
|
||||||
let imageContainer: HTMLDivElement;
|
let imageContainer: HTMLDivElement;
|
||||||
@@ -17,6 +17,9 @@
|
|||||||
let currentImageIdx = $state<number | null>(images.length > 0 ? 0 : null);
|
let currentImageIdx = $state<number | null>(images.length > 0 ? 0 : null);
|
||||||
let initialCurrentImageIdxEffect = $state(false);
|
let initialCurrentImageIdxEffect = $state(false);
|
||||||
|
|
||||||
|
let activeImageIdx = $state<number | null>(null);
|
||||||
|
let activeImageHtmlDialog = $state<HTMLDialogElement | null>(null);
|
||||||
|
|
||||||
// lifecycle
|
// lifecycle
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
// this check must be first. otherwise this effect will not be called besides on mount
|
// this check must be first. otherwise this effect will not be called besides on mount
|
||||||
@@ -39,14 +42,21 @@
|
|||||||
if (currentImageIdx == null || currentImageIdx == images.length - 1) return;
|
if (currentImageIdx == null || currentImageIdx == images.length - 1) return;
|
||||||
currentImageIdx += 1;
|
currentImageIdx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function showImagePopup(imageIndex: number) {
|
||||||
|
activeImageIdx = imageIndex;
|
||||||
|
// wait for dialog dom element
|
||||||
|
await tick();
|
||||||
|
activeImageHtmlDialog!.showModal();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="carousel w-full aspect-video rounded items-center overflow-hidden" bind:this={imageContainer}>
|
<div class="carousel w-full aspect-video rounded items-center overflow-hidden" bind:this={imageContainer}>
|
||||||
{#each images as image (image.path)}
|
{#each images as image, i (image.path)}
|
||||||
<div class="carousel-item w-full">
|
<button class="carousel-item w-full cursor-zoom-in z-10" onclick={() => showImagePopup(i)}>
|
||||||
<img src={image.path} class="w-full object-cover" alt={image.text ?? image.path} />
|
<img src={image.path} class="w-full object-cover" alt={image.text ?? image.path} />
|
||||||
</div>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if currentImageIdx != null}
|
{#if currentImageIdx != null}
|
||||||
@@ -62,17 +72,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="absolute top-0 left-2 h-full flex items-center">
|
<div class="absolute top-0 left-2 h-full flex items-center">
|
||||||
<button
|
<button
|
||||||
class="backdrop-invert-75 w-10 h-10 rounded-full flex justify-center items-center cursor-pointer"
|
class="backdrop-invert-75 w-10 h-10 rounded-full flex justify-center items-center cursor-pointer z-20"
|
||||||
aria-label="previous"
|
aria-label="previous"
|
||||||
onclick={scrollPrev}><span class="iconify iconify-[heroicons--chevron-left] w-2/5 h-2/5"></span></button
|
onclick={scrollPrev}><span class="iconify iconify-[heroicons--chevron-left] w-2/5 h-2/5"></span></button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute top-0 right-2 h-full flex items-center">
|
<div class="absolute top-0 right-2 h-full flex items-center">
|
||||||
<button
|
<button
|
||||||
class="backdrop-invert-75 w-10 h-10 rounded-full flex justify-center items-center cursor-pointer"
|
class="backdrop-invert-75 w-10 h-10 rounded-full flex justify-center items-center cursor-pointer z-20"
|
||||||
aria-label="next"
|
aria-label="next"
|
||||||
onclick={scrollNext}><span class="iconify iconify-[heroicons--chevron-right] w-2/5 h-2/5"></span></button
|
onclick={scrollNext}><span class="iconify iconify-[heroicons--chevron-right] w-2/5 h-2/5"></span></button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if activeImageIdx != null}
|
||||||
|
<dialog
|
||||||
|
class="modal"
|
||||||
|
bind:this={activeImageHtmlDialog}
|
||||||
|
onclose={() => setTimeout(() => (activeImageIdx = null), 300)}
|
||||||
|
>
|
||||||
|
<div class="max-w-4/6 max-h-5/6 modal-box bg-transparent p-0 rounded-none">
|
||||||
|
<img class="object-cover" src={images[activeImageIdx].path} alt={images[activeImageIdx].path} />
|
||||||
|
</div>
|
||||||
|
<form method="dialog" class="modal-box w-[initial] h-[initial] bg-transparent p-0 absolute top-5 right-5">
|
||||||
|
<button class="text-2xl cursor-pointer">✖</button>
|
||||||
|
</form>
|
||||||
|
<form method="dialog" class="modal-backdrop">
|
||||||
|
<button class="cursor-default">close</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user