纯JavaScript中如何为轮播触摸滑动事件正确实现防抖限制触发频率?
核心错误说明
你所有写法的共同问题有两个:
- 防抖函数参数传递错误:
debounce/_.debounce的第一个参数要求是函数本身,你写成debounce(carousel.next(), 3000)相当于立即执行了carousel.next(),把执行结果传给防抖函数,不仅不会生效,还会触发「Expected a function」的报错。 - 防抖函数实例创建时机错误:防抖函数需要提前创建、共享同一个定时器实例,你在事件回调内部每次调用
debounce都会生成全新的防抖实例,每个实例的定时器独立,完全达不到限制触发频率的效果。
修复方案
方案1:原生手写实现(无第三方依赖)
你原本的防抖函数逻辑没有问题,只需要调整使用方式即可:
const debounce = (func, delay) => { let debounceTimer return function () { const context = this const args = arguments clearTimeout(debounceTimer) debounceTimer = setTimeout(() => func.apply(context, args), delay) } } // 提前创建防抖后的切换函数,共享定时器 const debouncedNext = debounce(() => carousel.next(), 3000) const debouncedPrev = debounce(() => carousel.previous(), 3000) if (location.pathname === "/") { const backgroundElement = document.querySelector('.carousel'); const hammer = new Hammer(backgroundElement) hammer.on('panright panleft', function (event) { event.preventDefault(); if (event.deltaX > 250) { debouncedNext(); } if (event.deltaX < -250) { debouncedPrev(); } }) }
如果你需要的是「3秒内无论触发多少次滑动,只执行一次切换」的效果,更适合用节流实现,原生节流参考:
const throttle = (func, delay) => { let lastCall = 0 return function(...args) { const now = Date.now() if (now - lastCall < delay) return lastCall = now return func(...args) } } // 使用方式和防抖完全一致,替换变量名即可 const throttledNext = throttle(() => carousel.next(), 3000) const throttledPrev = throttle(() => carousel.previous(), 3000)
方案2:Lodash实现
调整参数传递和创建时机即可解决报错:
// 提前创建防抖/节流后的切换函数,用throttle更贴合3秒仅触发一次的需求 const throttledNext = _.throttle(() => carousel.next(), 3000) const throttledPrev = _.throttle(() => carousel.previous(), 3000) if (location.pathname === "/") { const backgroundElement = document.querySelector('.carousel'); const hammer = new Hammer(backgroundElement) hammer.on('panright panleft', function (event) { event.preventDefault(); if (event.deltaX > 250) { throttledNext(); } if (event.deltaX < -250) { throttledPrev(); } }) }
方案3:RxJS实现
你的RxJS代码主要问题是没有将Hammer事件转换为Observable、操作符使用和语法错误,修复后参考:
import { fromEvent } from 'rxjs'; import { debounceTime, map, filter } from 'rxjs/operators'; if (location.pathname === "/") { const backgroundElement = document.querySelector('.carousel'); const hammer = new Hammer(backgroundElement); // 转换Hammer事件为Observable fromEvent(hammer, 'panright panleft').pipe( debounceTime(3000), map(event => event.deltaX), filter(deltaX => Math.abs(deltaX) > 250) ).subscribe(deltaX => { deltaX > 250 ? carousel.next() : carousel.previous(); }) }
内容的提问来源于stack exchange,提问作者Munchkin
相关产品推荐
相关产品推荐

