如何在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
相关产品推荐
相关产品推荐

