移动端相册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; }
问题分析
- 重复事件绑定:在
photos.forEach循环中,每遍历一张图片就给popup_img绑定一次touchstart事件,最终只有最后一张图片的索引会被闭包保留。触发触摸事件时,currentImgIndex会被强制设为最后一张图的索引,完全偏离当前展示的图片。 - 错误的索引重置:
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
相关产品推荐
相关产品推荐

