使用hammer.js实现Tinder式滑动后,事件多次触发问题求助
解决Hammer.js实现Tinder卡片滑动时事件多次触发的问题
问题分析
你遇到的事件多次触发问题,主要源于以下几点:
- 已标记为
removed的卡片仍保留Hammer事件绑定,后续操作可能意外触发其panend回调 - 缺少状态控制,同一卡片在滑动动画未完成时,可能被重复触发事件逻辑
- 滑动完成后的业务函数没有防重复执行的限制
解决方案
1. 添加状态锁,避免同一卡片重复触发
在panend事件处理开头,检查卡片是否正在处理,若已在处理则直接返回,防止重复执行。
2. 销毁已移除卡片的Hammer实例
当卡片被标记为removed后,销毁对应的Hammer实例,彻底解绑事件,避免无效触发。
3. 优化事件绑定逻辑
确保每个卡片仅绑定一次Hammer事件,避免因重复初始化导致的多事件绑定。
修改后的代码
var allCards = document.querySelectorAll('.match--card'); var nope = document.getElementById('nope'); var love = document.getElementById('love'); allCards.forEach(function (el) { var hammertime = new Hammer(el); // 标记卡片是否正在处理滑动逻辑 let isProcessing = false; hammertime.on('pan', function (event) { el.classList.add('moving'); }); hammertime.on('pan', function (event) { if (event.deltaX === 0) return; if (event.center.x === 0 && event.center.y === 0) return; matchContainer.classList.toggle('match_love', event.deltaX > 0); matchContainer.classList.toggle('match_nope', event.deltaX < 0); var xMulti = event.deltaX * 0.03; var yMulti = event.deltaY / 80; var rotate = xMulti * yMulti; event.target.style.transform = 'translate(' + event.deltaX + 'px, ' + event.deltaY + 'px) rotate(' + rotate + 'deg)'; }); hammertime.on('panend', function (event) { // 如果正在处理,直接返回 if (isProcessing) return; isProcessing = true; var direction = event.direction; el.classList.remove('moving'); matchContainer.classList.remove('match_love'); matchContainer.classList.remove('match_nope'); var moveOutWidth = document.body.clientWidth; var keep = Math.abs(event.deltaX) < 80 || Math.abs(event.velocityX) < 0.5; event.target.classList.toggle('removed', !keep); if (keep) { event.target.style.transform = ''; // 处理完成,解锁 isProcessing = false; } else { var endX = Math.max(Math.abs(event.velocityX) * moveOutWidth, moveOutWidth); var toX = event.deltaX > 0 ? endX : -endX; var endY = Math.abs(event.velocityY) * moveOutWidth; var toY = event.deltaY > 0 ? endY : -endY; var xMulti = event.deltaX * 0.03; var yMulti = event.deltaY / 80; var rotate = xMulti * yMulti; event.target.style.transform = 'translate(' + toX + 'px, ' + (toY + event.deltaY) + 'px) rotate(' + rotate + 'deg)'; // 仅在有效滑动时执行业务函数 if (direction === Hammer.DIRECTION_LEFT) { console.log('direction nope'); bubble_fn_swipeNope() } else if (direction === Hammer.DIRECTION_RIGHT) { console.log('direction love') bubble_fn_swipeLove() } var activeCards = document.querySelectorAll('.match--card:not(.removed)').length; bubble_fn_activeCards(activeCards); // 销毁已移除卡片的Hammer实例,解绑事件 hammertime.destroy(); // 等待动画完成后再初始化新卡片,避免时序问题 setTimeout(() => { initCards(); // 处理完成,解锁 isProcessing = false; }, 300); } }); }); function initCards(card, index) { var newCards = document.querySelectorAll('.match--card:not(.removed)'); if (!newCards.length) { bubble_fn_activeCards(0); } else { var cL = newCards.length; bubble_fn_activeCards(cL); var card= newCards[0]; var cardId = card.id; bubble_fn_activeCardUser(cardId); } newCards.forEach(function (card, index) { card.style.zIndex = allCards.length - index; card.style.transform = 'scale(' + (20 - index) / 20 + ') translateY(-' + 30 * index + 'px)'; card.style.opacity = (10 - index) / 10; }); matchContainer.classList.add('loaded'); } initCards();
关键修改说明
- 新增
isProcessing状态锁,防止同一卡片在处理过程中被重复触发 - 使用
Hammer.DIRECTION_LEFT/Hammer.DIRECTION_RIGHT常量替代硬编码数字,提高可读性和稳定性 - 卡片移除后调用
hammertime.destroy()销毁实例,彻底解绑事件 - 用
setTimeout等待滑动动画完成后再初始化新卡片,避免时序问题导致的意外触发
内容的提问来源于stack exchange,提问作者mm1975
相关产品推荐
相关产品推荐

