如何将这个“滚轮”事件用于触摸和/或拖动?

问题描述 投票:0回答:0

目前正在开发 Nuxt 3 应用程序,该应用程序包含目前仅适用于水平滚动的轮播功能。但是,我遇到了一个问题,即只有拥有触控板的用户才能滚动。

为了解决这个问题,我想出了实现“抓取和滚动”事件的想法,允许用户通过单击并向左或向右拖动来滚动轮播。

我尝试了多种解决方案,但我目前的代码如下:

const carousel: any = ref();
const items: any = reactive([]);
const images: any = reactive([]);
const progressLine: any = ref();
const progress: any = ref(0);
const progressContent: any = ref(0);
const tweened = reactive({
    number: 0,
});
const duration = reactive({ number: 1 });

onMounted(async () => {
    await nextTick();

    let carouselWidth: any = carousel.value.clientWidth;
    let itemWidth: any = items[0].clientWidth;
    let itemPadding: any = 16 * (items.length - 1);
    let wrapWidth: any = items.length * itemWidth + itemPadding;
    let scrollPosition = 0;
    let maxScrollPosition: any;

    // Calculate the maximum scroll position
    maxScrollPosition = wrapWidth - carouselWidth;

    let handleScrollEvent = (e: { deltaX: any }) => {
        const { deltaX } = e;

        // Update the scroll position based on the mousewheel delta
        scrollPosition += deltaX;

        // Clamp the scroll position to prevent scrolling beyond the carousel edges
        scrollPosition = Math.max(0, Math.min(scrollPosition, maxScrollPosition));

        // Calculate the progress of the scroll position as a percentage
        progress.value = (scrollPosition / maxScrollPosition) * 100;
        progressContent.value = progress.value;

        // Use GSAP to animate the carousel to the new scroll position
        gsap.to(carousel.value, { duration: duration.number, ease: 'expo.out', x: -scrollPosition });

        // Update the progress line width to match the scroll position
        gsap.to(progressLine.value, { duration: duration.number, ease: 'expo.out', width: `${progress.value}%` });
    };

    let handleTouchEvent = (e: {
        deltaX: any,
        touches: {
            clientX: any,
            0: { clientX: any },
        }[],
    }) => {
        const { touches } = e;

        // Get the touch position
        const touchPosition = touches[0].clientX;

        // Update the scroll position based on the touch delta
        scrollPosition += touchPosition;

        // Clamp the scroll position to prevent scrolling beyond the carousel edges
        scrollPosition = Math.max(0, Math.min(scrollPosition, maxScrollPosition));

        // Calculate the progress of the scroll position as a percentage
        progress.value = (scrollPosition / maxScrollPosition) * 100;
        progressContent.value = progress.value;

        // Use GSAP to animate the carousel to the new scroll position
        gsap.to(carousel.value, { duration: duration.number, ease: 'expo.out', x: -scrollPosition });

        // Update the progress line width to match the scroll position
        gsap.to(progressLine.value, { duration: duration.number, ease: 'expo.out', width: `${progress.value}%` });
    };

    carousel.value.addEventListener('wheel', handleScrollEvent);
    carousel.value.addEventListener('touchmove', handleTouchEvent);

    window.addEventListener('resize', () => {
        carouselWidth = carousel.value.clientWidth;
        itemWidth = items[0].clientWidth;
        itemPadding = 16 * items.length;
        wrapWidth = items.length * itemWidth + itemPadding;
    });
});
<ul class="slider" ref="carousel">
    <li v-for="(project, i) in fetchedProjects" :key="project.slug" class="project-item" ref="items">
        <NuxtLink ref="project" v-if="project.img" class="project-link">
            <div class="project-figure">
                <img
                    :src="'https://picsum.photos/5' + i.toString().padStart(2, '0')"
                    :alt="project.description"
                    class="project-figure_img"
                    ref="images"
                />
                <span class="project-figure_span">{{ project.name }}</span>
            </div>
        </NuxtLink>
    </li>
</ul>
ul.slider {
    --imgSize: 14.6875rem;
    height: 100%;
    display: inline-flex;
    flex-wrap: nowrap;
    li.project-item {
        flex: 0 0 var(--imgSize);
        &:not(:last-of-type) {
            margin-right: 1em;
        }
        div.project-figure {
            user-select: none;

            img {
                pointer-events: none;
                user-select: none;
                height: var(--imgSize);
                width: var(--imgSize);
                object-fit: cover;
            }
            span {
                display: none;
            }
        }
    }
}

但是,它没有按预期工作。每次我尝试滚动旋转木马时,它都会重置到起始位置,就好像滚动是相对于窗口大小而不是旋转木马本身一样。这种行为很奇怪。

虽然“水平滚动”事件有效。

javascript vue.js carousel touch-event gsap
© www.soinside.com 2019 - 2024. All rights reserved.