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

React图片滑块滚动函数始终定位同一位置问题排查

React图片滑块切换点点击异常问题

我用React开发图片滑块,切换点数量和图片卡片一致,但滚动函数仅首次点击有效,且无论点击哪个切换点,滑块都会定位到前两张卡片的中间位置(比如点击第四个切换点,仍滚到第一张与第二张中间)。怀疑问题出在magicWallOffsetLeft的计算逻辑或CSS宽度设置上,但无法确定具体原因。


相关代码

Work.js组件

import React, { useEffect, useState } from 'react'
import './Work.css'
import Cta from '../atoms/Cta'
import Headphone from '../assets/images/headphones3.png'
import Calendar from '../assets/images/calendar.png'
import DevConnector from '../assets/images/dev.png'
import Bulls from '../assets/images/bulls2.png'
import Expenses from '../assets/images/reactExpenses.png'
import SixFlags from '../assets/images/sixflags.png'
import useDots from '../hooks/useDots';

const images = [Calendar, Headphone, SixFlags, DevConnector, Bulls, Expenses];

const Work = () => {
    const [activeIndex, setActiveIndex] = useDots(images.length);
    const [showDots, setShowDots] = useState(false);

    useEffect(() => {
        function handleResize() {
          setShowDots(window.innerWidth <= 810);
        }
    
        handleResize();
        window.addEventListener('resize', handleResize);
    
        return () => {
          window.removeEventListener('resize', handleResize);
        };
      }, []);

    function handleDotClick(index) {
        console.log("Clicked dot with index:", index);
      
        setActiveIndex(index);
      
        const targetCard = document.querySelector(`[data-id="${index}"]`);
        console.log("Target card:", targetCard);

        if (targetCard) {
          const magicWall = document.querySelector('#home-magicwall ul');
          const magicWallRect = magicWall.getBoundingClientRect();
          const magicWallOffsetLeft = magicWallRect.left + window.scrollX;
          console.log("Magic wall:", magicWall);
          console.log("Target card offsetLeft:", targetCard.offsetLeft);
          console.log("Magic wall offsetLeft:", magicWall.offsetLeft);
      
          magicWall.scroll({
            left: targetCard.offsetLeft - magicWallOffsetLeft,
            behavior: 'smooth'
          });
        }
      }
  
    return (
      <div className="main-container">
        <section id="work">
          <div id="header">
            <h2>My Work</h2>
          </div>
          <div className="text-zone-2">
            <div>
              <p>
                A small gallery of recent projects chosen by me. I've done them all together with amazing people from
                companies around the globe. It's only a drop in the ocean compared to the entire list. Interested to see
                some more? Visit my work page.
                <br />
              </p>
            </div>
            <div className="btn-container">
              <Cta className="btn" link="https://github.com/mateoghidini1998" content="See More" />
            </div>
          </div>
          <div className="fake-big-2">Work</div>
        </section>
        {showDots && (
          <div className="dots">
            {images.map((image, index) => (
              <button
                key={index}
                className={`dot ${index === activeIndex ? 'active' : ''}`}
                onClick={() => handleDotClick(index)}
              ></button>
            ))}
          </div>
        )}
        <div id="home-magicwall" className="fake-magicwall">
          <ul>
            {images.map((image, index) => (
              <li key={index} className={`magic-wall_item ${index === activeIndex ? 'active' : ''}`}>
                <img src={image} alt="image" />
                <div className="hover-content"></div>
                <a href="/" className="colorbox" data-id={index}></a>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
};

export default Work;

useDots.js自定义钩子

import { useState } from 'react';

function useDots(images) {
  const [activeIndex, setActiveIndex] = useState(0);

  return [activeIndex, setActiveIndex];
}

export default useDots;

部分CSS代码

.fake-magicwall {
    background-color: rgba(255, 255, 255, 0.1);
    position: relative;
  }
  
  .fake-magicwall ul {
    list-style: none;
    display: flex;
    display: -ms-flexbox;
    display: -webkit-box;
    flex-wrap: wrap;
    margin: 0;
    padding: 0;
  }
  
  .fake-magicwall ul li {
    width: 50%;
    height: 550px;
    position: relative;
    overflow: hidden;
    display: list-item;
  }
  
  .fake-magicwall ul li:hover .magic-wall_item {
    transform: scale(1.14);
  }
  
  .magic-wall_item {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    transition: transform 0.5s, opacity 0.5s;
  }
  
  .fake-magicwall img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  
  @media only screen and (max-width: 810px) {
    .fake-magicwall ul {
      overflow: hidden;
      overflow-x: auto;
      flex-wrap: nowrap;
      width: 100%;
    }
  
    #home-magicwall {
      width: 100%;
      overflow-x: auto;
      position: relative;
    }
  
    .fake-magicwall ul li {
      width: 100%;
      flex-shrink: 0;
    }
  
    .dot {
      width: 15px;
      height: 15px;
      border-radius: 50%;
      margin: 10px;
    }
  }

问题原因及修复方案

1. 目标元素选择错误

你当前通过document.querySelector('[data-id="${index}"]')选中的是<a>标签,但这个标签是绝对定位在<li>内部的,它的offsetLeft是相对于父元素<li>的,而非<ul>,导致滚动位置计算完全错误。应该直接选择对应的<li>元素。

2. 滚动位置计算逻辑错误

scrollLeft是元素自身的滚动距离,不需要考虑元素在页面中的绝对位置,直接使用目标<li>相对于<ul>的offsetLeft即可,之前的magicWallOffsetLeft属于多余计算,反而导致位置偏移。

3. 优化React写法(推荐)

用useRef获取<ul>元素,替代document.querySelector,更符合React组件化规范,避免DOM结构变化引发的选择错误。

修复后的代码修改

Work.js组件修改

import React, { useEffect, useState, useRef } from 'react'
// 其他导入保持不变

const Work = () => {
    const [activeIndex, setActiveIndex] = useDots(images.length);
    const [showDots, setShowDots] = useState(false);
    const magicWallRef = useRef(null); // 添加ref引用

    // handleResize的useEffect保持不变

    function handleDotClick(index) {
        console.log("Clicked dot with index:", index);
        setActiveIndex(index);

        // 直接获取对应的li元素
        const targetLi = magicWallRef.current?.children[index];
        if (targetLi && magicWallRef.current) {
            magicWallRef.current.scroll({
                left: targetLi.offsetLeft,
                behavior: 'smooth'
            });
        }
    }

    return (
      <div className="main-container">
        {/* 其他内容保持不变 */}
        <div id="home-magicwall" className="fake-magicwall">
          {/* 给ul添加ref属性 */}
          <ul ref={magicWallRef}>
            {images.map((image, index) => (
              <li key={index} className={`magic-wall_item ${index === activeIndex ? 'active' : ''}`}>
                <img src={image} alt="image" />
                <div className="hover-content"></div>
                <a href="/" className="colorbox"></a> {/* 可删除data-id属性 */}
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
};

export default Work;

CSS调整(可选)

确保移动端下<ul>的宽度自适应所有<li>的总宽度,避免滚动异常:

@media only screen and (max-width: 810px) {
  .fake-magicwall ul {
    overflow-x: auto;
    flex-wrap: nowrap;
    width: fit-content; /* 替换原width:100%,让ul宽度等于所有li的总宽度 */
  }
  /* 其他样式保持不变 */
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:35:31