diff --git a/src/modules/carousel.js b/src/modules/carousel.js index 4bfbf840..ca517939 100644 --- a/src/modules/carousel.js +++ b/src/modules/carousel.js @@ -331,13 +331,27 @@ layui.define(['jquery', 'lay'], function(exports){ if(options.elem.data('haveEvents')) return; // 移入移出容器 - options.elem.on('mouseenter', function(){ + options.elem.on('mouseenter touchstart', function(){ if (that.config.autoplay === 'always') return; clearInterval(that.timer); - }).on('mouseleave', function(){ + }).on('mouseleave touchend', function(){ if (that.config.autoplay === 'always') return; that.autoplay(); }); + + var touchEl = options.elem; + var isVertical = options.anim === 'updown'; + lay.touchSwipe(touchEl, { + onTouchEnd: function(e, state){ + var duration = Date.now() - state.timeStart; + var delta = isVertical ? state.deltaY : state.deltaX; + var speed = delta / duration; + var shouldSwipe = Math.abs(speed) > 0.25 || Math.abs(delta) > touchEl[isVertical ? 'height' : 'width']() / 3; + if(shouldSwipe){ + that.slide(delta > 0 ? '' : 'sub'); + } + } + }) options.elem.data('haveEvents', true); }; diff --git a/src/modules/lay.js b/src/modules/lay.js index 66adadeb..cda5d3b0 100644 --- a/src/modules/lay.js +++ b/src/modules/lay.js @@ -505,6 +505,110 @@ } }; + /** + * 检测是否支持 Passive Event Listeners + * 引用自 https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + * @type {boolean} + */ + lay.passiveSupported = function(){ + var passiveSupported = false; + try { + var opts = Object.defineProperty({}, 'passive', { + get: function() { + passiveSupported = true; + } + }); + window.addEventListener('test', null, opts); + window.removeEventListener('test', null, opts); + } catch (err) {} + return passiveSupported; + }(); + + /** + * 是否支持 touch 事件 + */ + lay.touchEventsSupported = function(){ + return 'ontouchstart' in window; + }; + + /** + * @typedef touchSwipeState + * @prop {{x: number,y: number}} coordsStart - 初始坐标 + * @prop {{x: number,y: number}} coordsEnd - 结束坐标 + * @prop {number} deltaX - X 轴变化量 + * @prop {number} deltaY - Y 轴变化量 + * @prop {'none'|'right'|'left'|'up'|'down'} direction - 滑动方向 + * @prop {Date} timeStart 开始时间 + */ + /** + * @callback touchSwipeCallback + * @param {TouchEvent} e 滑动事件 + * @param {touchSwipeState} state 滑动相关的状态 + */ + /** + * 基于 touch 事件的触摸滑动 + * @param {string | HTMLElement | JQuery} elem - HTML 元素 + * @param {{onTouchStart?: touchSwipeCallback, onTouchMove?: touchSwipeCallback, onTouchEnd?: touchSwipeCallback}} opts - 配置项 + */ + lay.touchSwipe = function(elem, opts){ + var options = opts + var targetElem = lay(elem)[0]; + + if(!targetElem || !lay.touchEventsSupported()) return; + + var state = { + coordsStart: {x:0, y:0}, + coordsEnd: {x:0, y:0}, + deltaX: 0, + deltaY: 0, + direction:'none', // 'up','down','left','right','none + timeStart: null + } + + var onStart = function(e){ + if(e.touches.length !== 1) return; + bindEvents(); + state.timeStart = Date.now(); + state.coordsStart.x = state.coordsEnd.x = e.touches[0].clientX; + state.coordsStart.y = state.coordsEnd.y = e.touches[0].clientY; + + options.onTouchStart && options.onTouchStart(e, state); + } + + var onMove = function(e){ + e.preventDefault(); + state.coordsEnd.x = e.touches[0].clientX; + state.coordsEnd.y = e.touches[0].clientY; + state.deltaX = state.coordsStart.x - state.coordsEnd.x; + state.deltaY = state.coordsStart.y - state.coordsEnd.y; + if(Math.abs(state.deltaX) > Math.abs(state.deltaY)){ + state.direction = state.deltaX > 0 ? 'left' : 'right'; + }else{ + state.direction = state.deltaY > 0 ? 'up' : 'down'; + } + options.onTouchMove && options.onTouchMove(e, state); + } + + var onEnd = function(e){ + options.onTouchEnd && options.onTouchEnd(e, state); + unbindEvents(); + } + + var bindEvents = function(){ + targetElem.addEventListener('touchmove', onMove, lay.passiveSupported ? { passive: false} : false); + targetElem.addEventListener('touchend', onEnd); + targetElem.addEventListener('touchcancel', onEnd); + } + + var unbindEvents = function(){ + targetElem.removeEventListener('touchmove', onMove); + targetElem.removeEventListener('touchend', onEnd, lay.passiveSupported ? { passive: false} : false); + targetElem.removeEventListener('touchcancel', onEnd); + } + + targetElem.addEventListener('touchstart', onStart); + } + /* * lay 元素操作