91 lines
2.5 KiB
Svelte
91 lines
2.5 KiB
Svelte
<script lang="ts">
|
|
import { spring } from 'svelte/motion';
|
|
import { onMount } from 'svelte';
|
|
|
|
export let items: any[] = []; // Array of components/items to display
|
|
|
|
let container: HTMLElement;
|
|
let isDragging = false;
|
|
let startX: number;
|
|
let scrollLeft: number;
|
|
|
|
const centerOffset = spring(0);
|
|
|
|
function handleMouseDown(e: MouseEvent) {
|
|
isDragging = true;
|
|
startX = e.pageX - container.offsetLeft;
|
|
scrollLeft = container.scrollLeft;
|
|
container.style.cursor = 'grabbing';
|
|
}
|
|
|
|
function handleMouseMove(e: MouseEvent) {
|
|
if (!isDragging) return;
|
|
e.preventDefault();
|
|
const x = e.pageX - container.offsetLeft;
|
|
const walk = (x - startX) * 2;
|
|
container.scrollLeft = scrollLeft - walk;
|
|
updateItemScales();
|
|
}
|
|
|
|
function handleMouseUp() {
|
|
isDragging = false;
|
|
container.style.cursor = 'grab';
|
|
}
|
|
|
|
function updateItemScales() {
|
|
const containerCenter = container.offsetWidth / 2;
|
|
$centerOffset = container.scrollLeft + containerCenter;
|
|
}
|
|
|
|
onMount(() => {
|
|
container.addEventListener('scroll', updateItemScales);
|
|
return () => container.removeEventListener('scroll', updateItemScales);
|
|
});
|
|
</script>
|
|
|
|
<div class="gallery-container"
|
|
bind:this={container}
|
|
on:mousedown={handleMouseDown}
|
|
on:mousemove={handleMouseMove}
|
|
on:mouseup={handleMouseUp}
|
|
on:mouseleave={handleMouseUp}>
|
|
|
|
{#each items as item, i}
|
|
<div class="gallery-item"
|
|
style="
|
|
--scale: {Math.max(0.6, 1 - Math.abs($centerOffset - (i * 300 + 150)) / 1000)};
|
|
--opacity: {Math.max(0.3, 1 - Math.abs($centerOffset - (i * 300 + 150)) / 1000)};
|
|
">
|
|
<svelte:component this={item} />
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
|
|
<style>
|
|
.gallery-container {
|
|
width: 100%;
|
|
overflow-x: scroll;
|
|
display: flex;
|
|
gap: 1rem;
|
|
padding: 2rem;
|
|
cursor: grab;
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
.gallery-container::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.gallery-item {
|
|
min-width: 300px;
|
|
height: 400px;
|
|
transform: scale(var(--scale, 1));
|
|
opacity: var(--opacity, 1);
|
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
</style> |