You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Svelte中实现双向水平无限手动滚动画廊?

无限水平图片画廊的双向滚动与初始滚动位置问题

我用Svelte+TailwindCSS开发一个支持手动双向滚动、无限循环展示的图片水平画廊。目前向右滚动时能正常添加图片,但完全不支持向左滚动。

我尝试让画廊初始滚动到中间位置,这样向左滚动可以立即生效,同时预加载前置图片,但不管怎么操作,scrollLeft都会在图片加载到画廊前更新,导致计算出来的scrollLeft始终是0。

求解决方法,相关学习资源也可以。


当前实现代码

主组件代码

<script lang='ts'>
    import Image from "$lib/image.svelte";
    import { imagesState } from "../store";
    import { onMount } from "svelte";

    let error = null;

    async function fetchImages() {
        try {
            const data = await fetch('/api/');
            if (!data.ok) throw new Error("Failed to feath images.");
            const files = await data.json();
            console.log('files:', files.tree);
            return files.tree;

        } catch (err: unknown) {
            if (err instanceof Error) {
                error = err.message;
            } else {
                error = String(err); // Handle unexpected error types
            }
        }
    }

    let gallery:HTMLElement | null = null;
    let scrollDirection: string;
    
    function handleScroll(event: Event) {
        if (Math.abs((event as WheelEvent).deltaX) > Math.abs((event as WheelEvent).deltaY) && Math.abs((event as WheelEvent).deltaX) > 0) {
            scrollDirection = (event as WheelEvent).deltaX > 0 ? "right" : "left";
            console.log("Scroll direction:", scrollDirection);
        }
    }

    function observeImages() {
        const observer = new IntersectionObserver(entries => {
        entries.forEach(entry => {
            entry.target.classList.toggle('show', entry.isIntersecting)
            if (!entry.isIntersecting) return;
            console.log('entry:', entry, "type:", typeof entry)
            const clone = entry.target.cloneNode(true) as Element;
            console.log('clone:', clone)
            gallery?.append(clone);
            observer.observe(clone);
            })
        }, {
            root: gallery,
            threshold: 0.2,
        })
        if(gallery){
            for(let child of gallery.children){
                observer.observe(child);
            }
        }
    }

    onMount(async () => {
        console.log("mount");
        gallery = document.getElementById('galleryCont');
        if(gallery) {
            gallery.addEventListener('wheel', handleScroll);
            gallery.addEventListener('touchmove', handleScroll)
        }
        await imagesState.set(await fetchImages())
        observeImages();
        return () => {
            if(gallery) {
                gallery.removeEventListener('wheel', handleScroll);
                gallery.removeEventListener('touchmove', handleScroll);
            }
        }
    });


</script>

<div id="bg" class="bg-green-400 h-screen w-full flex justify-center">
    <div id="content" class="bg-red-200 w-full flex items-center">
        <div id="galleryCont" class="bg-yellow-200 w-full h-1/2 flex flex-row justify-start overflow-x-scroll overflow-y-hidden">
            {#if $imagesState}
                {#each $imagesState as image}
                    <Image imgSrc={image['path']} />
                {/each}
            {/if}
        </div>
    </div>
</div>

全局CSS

.image {
    transform: translateY(300px);
    opacity: 0;
    transition: 300ms;
}

.image.show {
    transform: translateY(0px);
    opacity: 1;
}

Image组件代码

<script lang="ts">
    export let imgSrc:string = ""
    export let imgAlt:string = ""
</script>

<img src="/images/{imgSrc}" alt={imgAlt} class="rounded-sm object-contain gallery-item image"/>

已尝试的解决方案

  • 尝试在图片不再相交时从DOM移除,并调整scrollLeft对应值,接近实现但出现明显画面闪烁——即使使用svelte.tick()确保状态更新后再设置滚动值,仍会在一帧内显示错误图片。
  • 尝试禁用默认滚动行为,通过每次滚动交互移除/添加元素实现自定义滚动,做了节流处理后仍出现滚动效果加倍的问题:激活时移动一张图片,节流时间结束后再次移动。

内容的提问来源于stack exchange,提问作者Joshua Lilienthal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:02:10