Skips middle item when swipe

40 views Asked by At

What I've figured out is that it seems to run the slide to function twice upon swipe, and every once in a while I am able to get the right behavior, but it's so unpredictable. It seems to only work when I swipe just barely. I've tried changing the minimum swipe distance required, no dice. Please help!

const slider = document.querySelector('.slider');
const sliderContainer = document.querySelector('.sliderContainer');
const items = document.querySelectorAll('.item');
const rowGap = (window.innerWidth / 2); // Adjust this value based on your desired row gap

let currentIndex = 0;
let index = 0;
let touchStartX = 0;
let touchStartY = 0;

// Calculate the total width of all items, including the row gap
const totalItemsWidth = Array.from(items).reduce((total, item) => {
  const itemStyle = getComputedStyle(item);
  const itemWidth = item.offsetWidth + parseFloat(itemStyle.marginLeft) + parseFloat(itemStyle.marginRight);
  return total + itemWidth + rowGap;
}, 0);

// Set the width of the slider container to accommodate all items
sliderContainer.style.width = `${totalItemsWidth}px`;



function slideTo(index, swipe) {
  let translation;
if (currentIndex == 2 && swipe != true) {
    console.log(currentIndex + index);
    index = 0;
}

  if (index < 0 || index >= items.length) {
    return;
  }

  translation = index * (items[0].offsetWidth + rowGap);
  sliderContainer.style.transform = `translateX(-${translation}px)`;
  currentIndex = index;
  return;
}

function slideNext(swipe) {
  slideTo(currentIndex + 1, swipe);
}

function slidePrev(swipe) {
  slideTo(currentIndex - 1, swipe);
}

setInterval(slideNext, 8500); // Automatically slide every 4.5 seconds

slider.addEventListener('touchstart', handleTouchStart);
slider.addEventListener('touchmove', handleTouchMove);
slider.addEventListener('touchend', handleTouchEnd);

function handleTouchStart(event) {
  touchStartX = event.touches[0].clientX;
}

function handleTouchMove(event) {
    event.preventDefault(); // Prevent default scrolling behavior
    const touchX = event.touches[0].clientX;
    const touchDiff = touchX - touchStartX;
  
    console.log("hi")
  if (touchDiff > 50) {
    slidePrev(true);
  } else if (touchDiff < -50) {
    slideNext(true);
  }
}

function handleTouchEnd() {
  touchStartX = 0;
}
.slider {
    margin: auto;
    display: flex;
    width: fit-content;
    overflow: hidden;
    gap: 50vw;
    position: relative;
    left: 1%;
}
  
.sliderContainer {
    display: flex;
    transition: transform 0.3s ease;
    padding-bottom: 3vh;
    position: relative;
}

.sliderWrapper {
    height: auto;
    width: 99.5%;
    overflow: hidden;
    background-color: white;
    border: 1px solid #E28C4A;
}

.sliderWrapper h1 {
    font-size: 11vmin;
    margin: 15px 0 15px 0;
    color: #9D000D;
}

.item {
    flex: 0 0 300px;
    display: flex;
    flex-direction: column;
}

.item img {
    width: 97%;
    border-radius: 15px;
}

.item > button {
    width: 95%;
}

.item h2 {
    min-width: 340px;
    font-size: 30px;
    margin: 7px 0 15px 0;
}
<div class="sliderWrapper">
        <h1>What's Popular</h1>
    <div class="sliderContainer">
        <div class="slider">
            <div id="butterPecan" class="item">
                <div class="textWrapper">
                    <img src="Images/Butter-Pecan-Mobile.webp">
                    <h2>Butter Pecan</h2>
                </div>
                <button>Order ahead</button>
            </div>
            <div class="item">
                <div class="textWrapper">
                    <img id="mintChocolate" src="Images/Mint-Chocolate-Chip-Mobile.webp">
                    <h2>Mint Chocolate Chip</h2>
                </div>
                <button>Order ahead</button>
            </div>
            <div class="item">
                <div class="textWrapper">
                    <img id="peanutButter" src="Images/Peanut-Butter-Chippy-Mobile.webp">
                    <h2>Peanut Butter Chippy</h2>
                </div>
                <button>Order ahead</button>
            </div>
        </div>
    </div>
</div>

1

There are 1 answers

1
Lachcim On BEST ANSWER

You've set up your handleTouchMove function to fire every time the user moves their finger. Inside the function, you measure the displacement with respect to where the gesture started. If the displacement exceeds 50 pixels, you trigger a slide change.

Once the displacement has reached 50, every subsequent move triggers a change, which explains the behavior you observed:

touchDiff -11.25, no change
touchDiff -12.5, no change
touchDiff -20, no change
touchDiff -25, no change
touchDiff -27.5, no change
touchDiff -33.75, no change
touchDiff -43.75, no change
touchDiff -50, no change
touchDiff -51.25, switching to next slide
touchDiff -57.5, switching to next slide
touchDiff -60, switching to next slide
touchDiff -71.25, switching to next slide
touchDiff -72.5, switching to next slide
touchDiff -75, switching to next slide

Depending on what you believe is proper UX, you could either:

  • Reset the displacement to 0 each time a slide is triggered
  • Prevent more than one slide per gesture

To reset the displacement, set touchStartX to touchX. You don't need the touchend listener.

slider.addEventListener('touchstart', handleTouchStart);
slider.addEventListener('touchmove', handleTouchMove);

function handleTouchStart(event) {
    touchStartX = event.touches[0].clientX;
}

function handleTouchMove(event) {
    event.preventDefault();
    const touchX = event.touches[0].clientX;
    const touchDiff = touchX - touchStartX;

    if (touchDiff > 150) {
        slidePrev(true);
        touchStartX = touchX;
    }
    else if (touchDiff < -150) {
        slideNext(true);
        touchStartX = touchX;
    }
}

To prevent multiple slides, introduce a new variable to keep track of whether the user has swiped or not:

slider.addEventListener('touchstart', handleTouchStart);
slider.addEventListener('touchmove', handleTouchMove);

let canSlide = true;

function handleTouchStart(event) {
    touchStartX = event.touches[0].clientX;
    canSlide = true;
}

function handleTouchMove(event) {
    event.preventDefault();

    if (!canSlide) return;

    const touchX = event.touches[0].clientX;
    const touchDiff = touchX - touchStartX;

    if (touchDiff > 50) {
        slidePrev(true);
        canSlide = false;
    }
    else if (touchDiff < -50) {
        slideNext(true);
        canSlide = false;
    }
}