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

移动端相册touchMove切换图片异常问题求助

移动端图片画廊滑动切换异常修复

问题场景

已实现图片画廊功能:点击图片全屏展示,按钮切换上/下一张正常,但移动端滑动切换异常:选中第2张图片后,无法显示该图,仅能滑动切换到第1张或第3张。

问题代码

const photos = Array.from(document.querySelectorAll(".gallery_item img"));
const popup = document.querySelector(".popup");
const popup_close = document.querySelector(".popup_close");
const popup_img = document.querySelector(".popup_img");
const arrow_left = document.querySelector(".popup_arrow--left");
const arrow_right = document.querySelector(".popup_arrow--right");

let currentImgIndex=0;
let isDragging=false;
let startPos=0;
let currentTranslate=0;
let prevTranslate=0;
let animationID=0;


photos.forEach((photo, index) => {
    const showPopup = (e) => {
      popup.classList.remove("hidden");
      popup_img.src = e.target.src;
      currentImgIndex = index;
    };
    
    photo.addEventListener("click", showPopup);
  
    // touch events - 重复绑定导致的问题根源
    popup_img.addEventListener("touchstart", touchStart(index));
    popup_img.addEventListener("touchend", touchEnd);
    popup_img.addEventListener("touchmove", touchMove);
    

  });
 

const showNextImg = () => {
  if (currentImgIndex === photos.length - 1) {
    currentImgIndex = 0;
  } else {
    currentImgIndex++;
  }
  popup_img.src = photos[currentImgIndex].src;
};

const showPreviousImg = () => {
  if (currentImgIndex === 0) {
    currentImgIndex = photos.length - 1;
  } else {
    currentImgIndex--;
  }
  popup_img.src = photos[currentImgIndex].src;
};
const closePopup = () => {
  popup.classList.add("fade-out");
  setTimeout(() => {
    popup.classList.add("hidden");
    popup.classList.remove("fade-out");
  }, 300);
};

document.addEventListener("keydown", (e) => {
    if (!popup.classList.contains("hidden")) {
      if (e.code === "ArrowRight" || e.keyCode === 39) {
        showNextImg();
      }
  
      if (e.code === "ArrowLeft" || e.keyCode === 37) {
        showPreviousImg();
      }
  
      if (e.code === "Escape" || e.keyCode === 27) {
        closePopup();
      }
    }
  });
popup_close.addEventListener("click", closePopup);
arrow_right.addEventListener("click", showNextImg);
arrow_left.addEventListener("click", showPreviousImg);


function touchStart(index){
    return function (event) {
        currentImgIndex = index; // 强制覆盖当前索引,导致滑动逻辑错乱
        startPos = getPositionX(event);
        isDragging = true;
        popup.classList.add("grabbing");
    }
 }
 function touchMove(event){
    if(isDragging){
        const currentPosition = getPositionX(event);
        currentTranslate = prevTranslate + currentPosition - startPos;
    }
 }
function touchEnd(){
    isDragging = false;
    const movedBy = currentTranslate - prevTranslate;
    if(movedBy < -30 && currentImgIndex < photos.length - 1) currentImgIndex += 1 ;
    if(movedBy > 30 && currentImgIndex > 0) currentImgIndex -=  1;
    changePhotoByIndex();
    popup.classList.remove("grabbing");
 
}
function getPositionX(event){
    return event.type.includes("mouse") ? event.pageX : event.touches[0].clientX;
}
function animation() {
    if (isDragging) requestAnimationFrame(animation);
}
function changePhotoByIndex() {
    popup_img.src = photos[currentImgIndex].src;
    prevTranslate = currentTranslate;   
}

问题分析

  1. 重复事件绑定:在photos.forEach循环中,每遍历一张图片就给popup_img绑定一次touchstart事件,最终只有最后一张图片的索引会被闭包保留。触发触摸事件时,currentImgIndex会被强制设为最后一张图的索引,完全偏离当前展示的图片。
  2. 错误的索引重置:touchStart函数里强制覆盖currentImgIndex为循环的index,破坏了全局变量记录当前展示图片索引的逻辑,这是第2张图片无法正常滑动的核心原因。

修复后的代码

const photos = Array.from(document.querySelectorAll(".gallery_item img"));
const popup = document.querySelector(".popup");
const popup_close = document.querySelector(".popup_close");
const popup_img = document.querySelector(".popup_img");
const arrow_left = document.querySelector(".popup_arrow--left");
const arrow_right = document.querySelector(".popup_arrow--right");

let currentImgIndex=0;
let isDragging=false;
let startPos=0;
let currentTranslate=0;
let prevTranslate=0;
let animationID=0;


photos.forEach((photo, index) => {
    const showPopup = (e) => {
      popup.classList.remove("hidden");
      popup_img.src = e.target.src;
      currentImgIndex = index;
    };
    
    photo.addEventListener("click", showPopup);
  });

// 触摸事件移到循环外,仅绑定一次
popup_img.addEventListener("touchstart", touchStart);
popup_img.addEventListener("touchend", touchEnd);
popup_img.addEventListener("touchmove", touchMove);
 

const showNextImg = () => {
  if (currentImgIndex === photos.length - 1) {
    currentImgIndex = 0;
  } else {
    currentImgIndex++;
  }
  popup_img.src = photos[currentImgIndex].src;
};

const showPreviousImg = () => {
  if (currentImgIndex === 0) {
    currentImgIndex = photos.length - 1;
  } else {
    currentImgIndex--;
  }
  popup_img.src = photos[currentImgIndex].src;
};
const closePopup = () => {
  popup.classList.add("fade-out");
  setTimeout(() => {
    popup.classList.add("hidden");
    popup.classList.remove("fade-out");
  }, 300);
};

document.addEventListener("keydown", (e) => {
    if (!popup.classList.contains("hidden")) {
      if (e.code === "ArrowRight" || e.keyCode === 39) {
        showNextImg();
      }
  
      if (e.code === "ArrowLeft" || e.keyCode === 37) {
        showPreviousImg();
      }
  
      if (e.code === "Escape" || e.keyCode === 27) {
        closePopup();
      }
    }
  });
popup_close.addEventListener("click", closePopup);
arrow_right.addEventListener("click", showNextImg);
arrow_left.addEventListener("click", showPreviousImg);


function touchStart(event) {
    startPos = getPositionX(event);
    isDragging = true;
    popup.classList.add("grabbing");
}
 function touchMove(event){
    if(isDragging){
        const currentPosition = getPositionX(event);
        currentTranslate = prevTranslate + currentPosition - startPos;
    }
 }
function touchEnd(){
    isDragging = false;
    const movedBy = currentTranslate - prevTranslate;
    if(movedBy < -30 && currentImgIndex < photos.length - 1) currentImgIndex += 1 ;
    if(movedBy > 30 && currentImgIndex > 0) currentImgIndex -=  1;
    changePhotoByIndex();
    popup.classList.remove("grabbing");
 
}
function getPositionX(event){
    return event.type.includes("mouse") ? event.pageX : event.touches[0].clientX;
}
function animation() {
    if (isDragging) requestAnimationFrame(animation);
}
function changePhotoByIndex() {
    popup_img.src = photos[currentImgIndex].src;
    prevTranslate = currentTranslate;   
}

修复说明

  • 移除循环内的重复触摸事件绑定,改为在循环外仅绑定一次,避免闭包导致的索引错误。
  • 删除touchStart函数里的currentImgIndex = index语句,保留全局变量currentImgIndex记录当前展示的图片索引,确保滑动逻辑基于当前显示的图片执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:01:18