原生JS结合Redux实现多元素步长滑动滑块功能
按可见元素数步长切换的多元素滑块原生实现
你已经完成滑块静态结构搭建,以下是符合要求的完整实现,全程用原生JS编写,适配现有Redux和原生useState能力,无需引入React。
1. 补充SCSS必要属性
原有样式缺少滑动容器的溢出控制、动画过渡配置,调整后完整代码如下:
@import "../../../styles/settings.scss"; .root { padding: 2rem 0; display: flex; justify-content: center; .thumbnailNavigationWrapper { padding: 10px; display: flex; justify-content: space-between; align-items: center; width: 100%; height: 90px; border: 1px solid $new-furniture-border-color; .arrow { font-size: 20px; height: 70px; width: 30px; display: flex; align-items: center; cursor: pointer; &.disabled { opacity: 0.3; pointer-events: none; } } .thumbnailMenu { flex: 1; overflow: hidden; margin: 0 10px; } ul { position: relative; display: flex; list-style: none; margin: 0; padding: 0; transition: transform 0.3s ease-in-out; li { flex-shrink: 0; width: 150px; height: 70px; margin: 0 10px; a { opacity: 0.5; display: block; width: 100%; height: 100%; &.activeThumbnail { opacity: 1; } img { width: 100%; height: 100%; object-fit: cover; } } } } } }
2. 原生JS核心逻辑
核心逻辑点:
- 初始化自动计算容器宽度、单项宽度,得出单屏可见元素数作为滑动步长
- 每次滑动按步长修改列表的
translateX位移值,配合CSS实现平滑动画 - 自动判断滑动边界,到首尾位置自动禁用对应箭头
- 状态同步支持对接现有Redux和原生useState能力
// 按项目实际路径替换以下导入 import store from '@/redux/store'; import { setBrandSliderOffset } from '@/redux/slices/brandSlice'; import { useState } from '@/utils/native-hooks'; // 初始化本地状态 const [currentOffset, setCurrentOffset] = useState(0); const [maxOffset, setMaxOffset] = useState(0); const [visibleItemCount, setVisibleItemCount] = useState(0); // DOM节点获取 const wrapper = document.querySelector('.thumbnailNavigationWrapper'); const viewport = wrapper.querySelector('.thumbnailMenu'); const sliderList = viewport.querySelector('ul'); const [leftArrow, rightArrow] = wrapper.querySelectorAll('.arrow'); const allItems = sliderList.querySelectorAll('li'); // 单个滑块项占位宽度 = 元素宽度 + 左右margin const itemTotalWidth = allItems[0].offsetWidth + 20; // 计算滑块参数(初始化、窗口resize时调用) const calcSliderConfig = () => { const viewportWidth = viewport.offsetWidth; // 计算单屏可见元素数,向下取整 const visibleCount = Math.floor(viewportWidth / itemTotalWidth); setVisibleItemCount(visibleCount); // 计算最大可偏移距离,避免滑出最后一屏 const totalContentWidth = allItems.length * itemTotalWidth; const maxOff = Math.max(0, totalContentWidth - viewportWidth); setMaxOffset(maxOff); // 窗口缩放时修正偏移量,避免出现空白 if (currentOffset > maxOff) { doSlide(0, maxOff); } }; // 执行滑动 const doSlide = (direction, forcedOffset = null) => { let targetOffset = forcedOffset !== null ? forcedOffset : currentOffset; const slideStep = visibleItemCount * itemTotalWidth; if (forcedOffset === null) { if (direction === 'left') { targetOffset = Math.max(0, currentOffset - slideStep); } else { targetOffset = Math.min(maxOffset, currentOffset + slideStep); } } // 更新本地状态 setCurrentOffset(targetOffset); // 应用位移动画 sliderList.style.transform = `translateX(-${targetOffset}px)`; // 同步状态到Redux store.dispatch(setBrandSliderOffset(targetOffset)); // 更新箭头禁用状态 leftArrow.classList.toggle('disabled', targetOffset === 0); rightArrow.classList.toggle('disabled', targetOffset >= maxOffset); }; // 绑定事件 leftArrow.addEventListener('click', () => doSlide('left')); rightArrow.addEventListener('click', () => doSlide('right')); window.addEventListener('resize', calcSliderConfig); // 初始化 calcSliderConfig(); doSlide(null, 0);
3. 对接注意事项
- 原生useState直接替换导入路径即可,逻辑层不用做修改,兼容所有基于闭包/发布订阅实现的原生hooks
- Redux部分只需要在对应slice中新增存储滑块偏移量的字段,其他模块需要读取滑块状态直接从store取数即可
- 如果需要加点击缩略图激活、自动轮播等扩展逻辑,直接在现有逻辑上新增对应事件绑定即可,核心位移逻辑可以复用
效果参考

内容的提问来源于stack exchange,提问作者sicArchitect
相关产品推荐
相关产品推荐

