摘要:前言本次給大家分享的是常見的移動(dòng)端單點(diǎn)觸摸事件的設(shè)計(jì)思路及實(shí)踐。實(shí)現(xiàn)即手指滑動(dòng)事件,應(yīng)用場(chǎng)景如輪播圖左右滑動(dòng)切換,整屏頁面滑動(dòng)翻頁等,算是移動(dòng)端最常見的手勢(shì)之一了。
前言
本次給大家分享的是常見的移動(dòng)端單點(diǎn)觸摸事件的設(shè)計(jì)思路及實(shí)踐。
核心技術(shù)主要就是利用移動(dòng)端的以下3個(gè)觸摸事件,來模擬和實(shí)現(xiàn)自定義的手勢(shì)操作
touchstart:手指觸摸到屏幕的一瞬間觸發(fā)
touchmove:手指在屏幕上移動(dòng)時(shí)觸發(fā)
touchend:手指從屏幕上離開時(shí)觸發(fā)
概念梳理touch事件觸發(fā)時(shí),有3組數(shù)據(jù)可以獲得觸摸信息,可能大家會(huì)對(duì)這幾組數(shù)據(jù)有些混淆,我根據(jù)自己的理解來盡量用通俗的語言給大家解釋清楚
touches:整個(gè)屏幕上所有的觸摸點(diǎn)集合
targetTouches:當(dāng)前DOM元素上的所有觸摸點(diǎn)集合
changedTouches:相對(duì)上一次觸摸點(diǎn)發(fā)生變化的集合
我們先來看一張圖
如圖所示,我們?cè)诠?jié)點(diǎn)B上綁定touch事件,圓圈代表觸摸點(diǎn)。
此時(shí)節(jié)點(diǎn)B有3個(gè)觸摸點(diǎn),即targetTouches數(shù)組有3項(xiàng),分別儲(chǔ)存著觸摸點(diǎn)的信息,此時(shí)touches和targetTouches是相同的。
當(dāng)我們將手指3移出節(jié)點(diǎn)B(始終保持3個(gè)手指觸摸在屏幕上),那么touchmove事件觸發(fā),targetTouches只剩2項(xiàng),而touches依然有3項(xiàng),此時(shí)changedTouches只有一項(xiàng)(因?yàn)橹挥惺种?改變了)。
然后我們讓所有手指離開屏幕,那么此時(shí)touchend事件觸發(fā),touches只剩0項(xiàng),targetTouches剩0項(xiàng),changedTouches有3項(xiàng)(因?yàn)?個(gè)手指發(fā)生了變化)。
好了,理解這些概念,有助于我們理解代碼中何時(shí)改去哪個(gè)touch數(shù)組里面的值。
思路及實(shí)踐 taptap可以理解為點(diǎn)擊事件,和click不同的是,移動(dòng)端的click事件有大約300ms的延遲,這是因?yàn)闉g覽器要判斷是否為雙擊事件。
思路
touchstart:時(shí)記錄時(shí)間點(diǎn)以及觸摸點(diǎn)的x、y坐標(biāo)
touchend:計(jì)算此時(shí)與開始時(shí)的時(shí)間差,水平和垂直方向的偏移量
說明:時(shí)間差用來判斷用戶觸摸的時(shí)長,超過規(guī)定時(shí)間則tap事件無效;偏移量用來判斷用戶的觸摸事件內(nèi)是否有過移動(dòng)的痕跡,這里我們?cè)试S少量的偏移,因?yàn)槭种缚赡艹霈F(xiàn)抖動(dòng)的情況
實(shí)現(xiàn)
const tapDefaults = { time: 250, offset: 10 } export default function tap (node, a, b) { let st, sx, sy let opts, callback if (typeof a === "function") { callback = a opts = Object.assign({}, tapDefaults, b) } else { callback = b opts = Object.assign({}, tapDefaults, a) } node.addEventListener("touchstart", (e) => { e.preventDefault() // 組織瀏覽器默認(rèn)行為,防止觸摸過程頁面滾動(dòng) const touch = e.targetTouches[0] st = e.timeStamp sx = touch.pageX sy = touch.pageY }, false) node.addEventListener("touchend", (e) => { const touch = e.changedTouches[0] if ( // 若為長按,則將時(shí)間判定條件更改 e.timeStamp - st <= opts.time && Math.abs(touch.pageX - sx) <= opts.offset && Math.abs(touch.pageY - sy) <= opts.offset ) { callback && callback() } }, false) }doubletap
即雙擊事件,兩次點(diǎn)擊時(shí)間間隔不超過規(guī)定時(shí)間則視為有效。
思路
第一次有效點(diǎn)擊,記錄該狀態(tài),反之重置狀態(tài)
第二次有效點(diǎn)擊,觸發(fā)事件并重置狀態(tài)
若兩次時(shí)間間隔過長,重置狀態(tài)
實(shí)現(xiàn)
const tapDefaults = { time: 250, offset: 10 } function handler (node, inject) { let st, sx, sy node.addEventListener("touchstart", (e) => { e.preventDefault() const touch = e.targetTouches[0] st = e.timeStamp sx = touch.pageX sy = touch.pageY }, false) node.addEventListener("touchend", (e) => { const touch = e.changedTouches[0] inject({ time: e.timeStamp - st, offsetX: Math.abs(touch.pageX - sx), offsetY: Math.abs(touch.pageY - sy) }) }, false) } export function doubletap (node, a, b) { let opts, callback let status = 0 if (typeof a === "function") { callback = a opts = Object.assign({}, tapDefaults, b) } else { callback = b opts = Object.assign({}, tapDefaults, a) } handler(node, (info) => { if ( info.time <= opts.time && info.offsetX <= opts.offset && info.offsetY <= opts.offset ) { if (status === 0) { status = 1 // 時(shí)間間隔太長則重置狀態(tài) setTimeout(() => { status = 0 }, opts.time) } else if (status === 1) { callback && callback() status = 0 } } else { status = 0 } }) }longtap
即長按,手指按住超過規(guī)定時(shí)間視為有效,在手指離開時(shí)觸發(fā)。
思路
和tap事件思路一樣,只不過時(shí)間的判定條件變更一下,改為超過多長時(shí)間才觸發(fā)
實(shí)現(xiàn)
const longtapDefaults = { time: 350, offset: 10 } // 這里代碼邏輯和tap事件一樣 // 更改時(shí)間判定為: // e.timeStamp - st > opts.timepress
即按壓事件,按住超過規(guī)定時(shí)間自動(dòng)觸發(fā),注意和longtap不同的是,longtap需要等到手指離開時(shí)觸發(fā),而press在按壓時(shí)間達(dá)到規(guī)定值,自動(dòng)觸發(fā),此時(shí)手指還在屏幕上。
思路
touchstart:記錄此時(shí)的x、y坐標(biāo),并且開啟一個(gè)定時(shí)器,在規(guī)定時(shí)間后執(zhí)行回調(diào),默認(rèn)是350ms
touchmove:監(jiān)聽移動(dòng)過程,在事件觸發(fā)前,若出現(xiàn)偏移量過大,則取消定時(shí)器
touchend:取消定時(shí)器
分析:根據(jù)以上思路,若按壓時(shí)間短,則手指離開時(shí)定時(shí)器已取消,回調(diào)不會(huì)觸發(fā)。
實(shí)現(xiàn)
const pressDefaults = { time: 350, offset: 10 } export default function press (node, a, b) { let opts, callback, sx, sy let timer = null if (typeof a === "function") { callback = a opts = Object.assign({}, pressDefaults, b) } else { callback = b opts = Object.assign({}, pressDefaults, a) } node.addEventListener("touchstart", (e) => { e.preventDefault() const touch = e.targetTouches[0] sx = touch.pageX sy = touch.pageY timer = setTimeout(() => { callback && callback() }, opts.time) }, false) node.addEventListener("touchmove", (e) => { const touch = e.targetTouches[0] if ( Math.abs(touch.pageX - sx) > opts.offset || Math.abs(touch.pageY - sy) > opts.offset ) { clearTimeout(timer) } }, false) node.addEventListener("touchend", () => { clearTimeout(timer) }, false) }swipe
即手指滑動(dòng)事件,應(yīng)用場(chǎng)景如:輪播圖左右滑動(dòng)切換,整屏頁面滑動(dòng)翻頁等,算是移動(dòng)端最常見的手勢(shì)之一了。
思路
touchstart:記錄時(shí)間點(diǎn)和觸摸點(diǎn)位置
touchmove:實(shí)時(shí)判斷滑動(dòng)偏移量
touchend:計(jì)算滑動(dòng)速度和方向,條件判定是否觸發(fā)事件
分析:考慮到需要在滑動(dòng)過程做一些動(dòng)畫特效等操作,因此我們將滑動(dòng)中的事件暴露給用戶自定義,值得注意的是,若是要實(shí)時(shí)改變滑塊位置的話,最好不要截流或防抖,截流會(huì)造成滑動(dòng)卡頓的現(xiàn)象,而防抖會(huì)出現(xiàn)延遲同步滑動(dòng)操作的情況;另外對(duì)滑動(dòng)速度也進(jìn)行了處理,原則上用戶滑動(dòng)距離超過規(guī)定后即視為有效,然而為了更好的用戶體驗(yàn),我們判定,如果用戶在短時(shí)間內(nèi)滑動(dòng)速度非??斓脑挘惨暈橐淮斡行У牟僮?,不一定非要滑動(dòng)很長的距離
實(shí)現(xiàn)
const swipeDefaults = { direction: "horizontal", // vertical speed: 200, offset: 100, prevent: true, // touchmove: (offset) => {} } export default function swipe (node, a, b) { let opts, callback, sTime, sTouch, eTouch if (typeof a === "function") { callback = a opts = Object.assign({}, swipeDefaults, b) } else { callback = b opts = Object.assign({}, swipeDefaults, a) } node.addEventListener("touchstart", (e) => { if (opts.prevent) { e.preventDefault() } sTime = e.timeStamp sTouch = eTouch = e.targetTouches[0] }, false) if (typeof opts.touchmove === "function") { node.addEventListener("touchmove", (e) => { eTouch = e.targetTouches[0] if (opts.direction === "horizontal") { opts.touchmove(eTouch.pageX - sTouch.pageX) } else { opts.touchmove(eTouch.pageY - sTouch.pageY) } }, false) } node.addEventListener("touchend", (e) => { eTouch = e.changedTouches[0] let time = e.timeStamp - sTime let offset, direction if (opts.direction === "horizontal") { offset = eTouch.pageX - sTouch.pageX direction = offset > 0 ? "right" : "left" } else { offset = eTouch.pageY - sTouch.pageY direction = offset > 0 ? "down" : "up" } if ( Math.abs(offset) >= opts.offset || Math.abs(offset) / time * 1000 >= opts.speed ) { callback && callback(direction) } }, false) }結(jié)束語
通過以上的思路講解和代碼實(shí)現(xiàn),我們完成了一個(gè)單點(diǎn)觸控的移動(dòng)端手勢(shì)庫,是不是迫不及待的想要一睹為快、體驗(yàn)一番。
最后附上本次分享的源碼和文檔:https://github.com/ansenhuang/axe/blob/master/packages/touch/README.md
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/93723.html
摘要:中手勢(shì)原理分析與數(shù)學(xué)知識(shí)的實(shí)踐引言在這觸控屏的時(shí)代,人性化的手勢(shì)操作已經(jīng)深入了我們生活的每個(gè)部分。這篇博文主要是解析了移動(dòng)端常用手勢(shì)的原理,及從前端的角度學(xué)習(xí)過程中所使用的數(shù)學(xué)知識(shí)。 HTML5中手勢(shì)原理分析與數(shù)學(xué)知識(shí)的實(shí)踐 引言 在這觸控屏的時(shí)代,人性化的手勢(shì)操作已經(jīng)深入了我們生活的每個(gè)部分?,F(xiàn)代應(yīng)用越來越重視與用戶的交互及體驗(yàn),手勢(shì)是最直接且最為有效的交互方式,一個(gè)好的手勢(shì)交互,能...
摘要:去年年底自己搭了一個(gè)在移動(dòng)端的開發(fā)框架,感覺體驗(yàn)不是很好。路由懶加載首頁終于寫完了,以上這些就是我在移動(dòng)端體驗(yàn)優(yōu)化的實(shí)戰(zhàn)。去年年底自己搭了一個(gè)vue在移動(dòng)端的開發(fā)框架,感覺體驗(yàn)不是很好。上個(gè)星期又要做移動(dòng)端的項(xiàng)目了。所以我花了兩天時(shí)間對(duì)之前的那個(gè)開發(fā)框架做了以下優(yōu)化 自定義vuex-plugins-loading 路由切換動(dòng)畫 + keep alive 動(dòng)態(tài)管理緩存組件 better-sc...
摘要:單點(diǎn)手勢(shì)庫分析手勢(shì)是什么有哪些方法實(shí)現(xiàn)首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)??梢钥吹刂房偨Y(jié)這是我挺久之前做的移動(dòng)端單點(diǎn)手勢(shì)庫學(xué)習(xí)時(shí)參考劇中人你可以在這里找到我個(gè)人網(wǎng)站 單點(diǎn)手勢(shì)庫 分析 手勢(shì)是什么? 有哪些方法實(shí)現(xiàn)? 首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)。在我們?cè)谝苿?dòng)端需要一些交互的時(shí)候。難免有時(shí)候需要左滑右...
摘要:單點(diǎn)手勢(shì)庫分析手勢(shì)是什么有哪些方法實(shí)現(xiàn)首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)??梢钥吹刂房偨Y(jié)這是我挺久之前做的移動(dòng)端單點(diǎn)手勢(shì)庫學(xué)習(xí)時(shí)參考劇中人你可以在這里找到我個(gè)人網(wǎng)站 單點(diǎn)手勢(shì)庫 分析 手勢(shì)是什么? 有哪些方法實(shí)現(xiàn)? 首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)。在我們?cè)谝苿?dòng)端需要一些交互的時(shí)候。難免有時(shí)候需要左滑右...
摘要:單點(diǎn)手勢(shì)庫分析手勢(shì)是什么有哪些方法實(shí)現(xiàn)首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)??梢钥吹刂房偨Y(jié)這是我挺久之前做的移動(dòng)端單點(diǎn)手勢(shì)庫個(gè)人博客地址學(xué)習(xí)時(shí)參考劇中人 單點(diǎn)手勢(shì)庫 分析 手勢(shì)是什么? 有哪些方法實(shí)現(xiàn)? 首先我這里指的手勢(shì)是指我們?cè)谝苿?dòng)端進(jìn)行觸屏交互的時(shí)候,用戶操作的一些手勢(shì)。在我們?cè)谝苿?dòng)端需要一些交互的時(shí)候。難免有時(shí)候需要左滑右滑。 目前市面...
閱讀 3888·2021-10-13 09:39
閱讀 3888·2021-09-24 09:48
閱讀 1258·2021-09-01 10:30
閱讀 2585·2019-08-30 15:55
閱讀 1838·2019-08-29 16:39
閱讀 2351·2019-08-26 13:55
閱讀 3112·2019-08-26 12:23
閱讀 1693·2019-08-26 11:59