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

如何实现滑块按20vw步进滚动?解决快速滚动失效问题

问题:滑块滚动时仅按20vw步进,快速滚动异常修复

我希望滑块在滚动时每次移动20vw,慢速滚动时功能正常,但快速滚动会出现异常。尝试过防抖函数和evt.preventDefault(),都没解决问题。请问如何实现滑块仅按20vw步进滚动?

<div class="slider-container" id="slider-container">
  <div class="slider" id="slider">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <img src="https://plus.unsplash.com/premium_photo-1664474619075-644dd191935f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8aW1hZ2V8ZW58MHx8MHx8fDA%3D&w=1000&q=80" class="slider-image" id="slider-image">
    <img src="https://99designs-blog.imgix.net/blog/wp-content/uploads/2016/03/web-images.jpg?auto=format&q=60&w=1600&h=824&fit=crop&crop=faces" class="slider-image" id="slider-image">
    <img src="https://cdn.futura-sciences.com/sources/images/dossier/773/01-intro-773.jpg" class="slider-image" id="slider-image">
    <img src="https://imgv3.fotor.com/images/blog-cover-image/part-blurry-image.jpg" class="slider-image" id="slider-image">
    <img src="https://images.ctfassets.net/hrltx12pl8hq/3j5RylRv1ZdswxcBaMi0y7/b84fa97296bd2350db6ea194c0dce7db/Music_Icon.jpg" class="slider-image" id="slider-image">
    <img src="https://h5p.org/sites/default/files/h5p/content/1209180/images/file-6113d5f8845dc.jpeg" class="slider-image" id="slider-image">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <img src="https://www.referenseo.com/wp-content/uploads/2019/03/image-attractive-960x540.jpg" class="slider-image" id="slider-image">
    <div class="button-container">
      <button class="prev">Prev</button>
      <button class="next">Next</button>
    </div>
  </div>
</div>
* {
  margin: 0px;
  padding: 0px;
  box-sizing: border-box;
  font-size: 1rem;
}

.slider-page {
  height: 80vh;
  width: 100vw;
  position: absolute;
}

.slider-container {
  height: 100%;
  width: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.slider {
  display: flex;
  width: 100%;
  position: relative;
  top: 0px;
  left: 0px;
  overflow: hidden;
  overflow-y: scroll;
  scroll-behavior: smooth;
}

.slider-image {
  width: 20vw;
  padding: 20px;
  border-radius: 40px;
  object-fit: cover;
  transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
  @media (max-width: 768px) {
    width: 33vw;
    padding: 20px;
  }
}

.slide-transition {
  transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
}

.button-container {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  justify-content: space-between;
  width: 20vw;
}

.next,
.prev {
  padding: 10px;
  border-radius: 10px;
}
const slider = document.getElementById("slider");
const slides = document.querySelectorAll(".slider-image");
const sliderPage = document.getElementById("slider-page");

// Slide size logic
function updateSlides() {
  let smallWidth = window.innerWidth / 6;
  let mediumWidth = window.innerWidth / 3;
  let fullWidth = window.innerWidth / 2;
  slides.forEach((slide) => {
    let imagePosition = slide.getBoundingClientRect().x;
    if (
      (imagePosition >= smallWidth && imagePosition < mediumWidth) ||
      (imagePosition > fullWidth && imagePosition <= 5 * smallWidth)
    ) {
      slide.style.transform = "scale(0.6)";
      slide.style.opacity = "0.6";
    } else if (imagePosition >= mediumWidth && imagePosition <= fullWidth) {
      slide.style.transform = "scale(1)";
      slide.style.opacity = "1";
    } else {
      slide.style.transform = "scale(0.3)";
      slide.style.opacity = "0.3";
    }
  });
}

// Button sliding logic

let slidercontainer = document.getElementById("slider-container");
let nextbutton = document.querySelector(".next");
let prevbutton = document.querySelector(".prev");

let scrollAmountVW = 20;

nextbutton.onclick = () => {
  slider.scrollLeft += (scrollAmountVW * window.innerWidth) / 100;
  updateSlides();
};

prevbutton.onclick = () => {
  slider.scrollLeft -= (scrollAmountVW * window.innerWidth) / 100;
  updateSlides();
};

slider.addEventListener("scroll", () => {
  updateSlides();
});

slider.addEventListener("wheel", (evt) => {
  evt.preventDefault();

  const scrollDirection = evt.deltaY > 0 ? 1 : -1;

  slider.scrollLeft +=
    (scrollAmountVW * scrollDirection * window.innerWidth) / 100;
  updateSlides();
});

// Script launch order
document.addEventListener("DOMContentLoaded", function() {
  updateSlides();

  window.addEventListener("resize", () => {
    updateSlides();
  });
  window.addEventListener("scroll", () => {
    updateSlides();
  });
});

(请全屏运行代码片段查看预期效果)


解决方案:用节流控制滚动触发频率

快速滚动异常的核心原因是:鼠标滚轮事件会在短时间内连续触发多次,即便调用了preventDefault,每次事件仍会执行scrollLeft累加,导致一次滚动触发多次步进。用节流函数限制事件触发频率,就能确保指定时间内只执行一次滚动操作。

修改后的JavaScript代码

const slider = document.getElementById("slider");
const slides = document.querySelectorAll(".slider-image");

// Slide size logic
function updateSlides() {
  let smallWidth = window.innerWidth / 6;
  let mediumWidth = window.innerWidth / 3;
  let fullWidth = window.innerWidth / 2;
  slides.forEach((slide) => {
    let imagePosition = slide.getBoundingClientRect().x;
    if (
      (imagePosition >= smallWidth && imagePosition < mediumWidth) ||
      (imagePosition > fullWidth && imagePosition <= 5 * smallWidth)
    ) {
      slide.style.transform = "scale(0.6)";
      slide.style.opacity = "0.6";
    } else if (imagePosition >= mediumWidth && imagePosition <= fullWidth) {
      slide.style.transform = "scale(1)";
      slide.style.opacity = "1";
    } else {
      slide.style.transform = "scale(0.3)";
      slide.style.opacity = "0.3";
    }
  });
}

// Button sliding logic
let nextbutton = document.querySelector(".next");
let prevbutton = document.querySelector(".prev");
let scrollAmountVW = 20;

nextbutton.onclick = () => {
  slider.scrollLeft += (scrollAmountVW * window.innerWidth) / 100;
  updateSlides();
};

prevbutton.onclick = () => {
  slider.scrollLeft -= (scrollAmountVW * window.innerWidth) / 100;
  updateSlides();
};

slider.addEventListener("scroll", () => {
  updateSlides();
});

// 节流函数:限制事件触发频率
function throttle(func, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      return func(...args);
    }
  };
}

// 用节流包装滚轮事件处理函数
const handleWheel = throttle((evt) => {
  evt.preventDefault();
  const scrollDirection = evt.deltaY > 0 ? 1 : -1;
  slider.scrollLeft += (scrollAmountVW * scrollDirection * window.innerWidth) / 100;
  updateSlides();
}, 300); // 300ms间隔,可根据需求调整

slider.addEventListener("wheel", handleWheel);

// Script launch order
document.addEventListener("DOMContentLoaded", function() {
  updateSlides();
  window.addEventListener("resize", updateSlides);
});

关键修改点

  1. 新增throttle节流函数,限制滚轮事件在300ms内仅执行一次,避免快速滚动时连续触发多次步进
  2. 将滚轮事件处理函数用节流包装,确保每次滚动只触发一次20vw的位移
  3. 移除不必要的window.addEventListener("scroll", updateSlides),滑块内部的scroll事件已足够触发样式更新

额外优化

修改.slider的CSS样式,隐藏垂直滚动条避免干扰操作:

.slider {
  display: flex;
  width: 100%;
  position: relative;
  top: 0px;
  left: 0px;
  overflow: hidden;
  overflow-x: auto; /* 仅保留水平滚动 */
  scroll-behavior: smooth;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 19:34:50