Vue 編寫一個長按指令

英文:Obaseki Nosa? ?譯文:kingrychen

github.com/OFED/translation/issues/3



有沒有想過只需按住一個按鈕幾秒鐘就能在你的 Vue 應(yīng)用中觸發(fā)一個功能敞嗡?


有沒有想過創(chuàng)建一個按鈕研乒,按下一次就可以清除單次輸入(或者持續(xù)按住可以清除所有輸入)颈墅?


想過?太好了债沮,英雄所見略同。


本文就是講解如何在按下(或者按桌苎)一個按鈕時隔披,既執(zhí)行一個函數(shù),又清除輸入鸠姨。


首先歼狼,我會講解如何使用純 JS 實(shí)現(xiàn)。而后也會創(chuàng)建一個 Vue 指令享怀。


請系好安全帶羽峰。好戲在后頭呢。


原理


要實(shí)現(xiàn)長按添瓷,用戶需要按下并按住按鈕幾秒鐘梅屉。


想通過代碼模擬這一效果,我們需要在鼠標(biāo)“點(diǎn)擊”按下按鈕時鳞贷,啟動一個計時器監(jiān)聽用戶按下的時長坯汤,如果時間超過我們期望的時長,就執(zhí)行相應(yīng)的函數(shù)搀愧。


非常簡單惰聂!然而疆偿,我們需要知道用戶何時按住按鈕。


如何實(shí)現(xiàn)


當(dāng)用戶點(diǎn)擊按鈕時搓幌,在點(diǎn)擊事件之前會觸發(fā)另外兩個事件: mousedown 和 mouseup杆故。


當(dāng)用戶按下按鈕時觸發(fā) mousedown 事件,用戶松開按鈕時調(diào)用 mouseup 事件溉愁。


我們需要做的是:


  • mousedown 事件觸發(fā)時处铛,啟動計時器。

  • 一旦 mouseup 事件在預(yù)期的 2 秒前被觸發(fā)拐揭,就清除計時器撤蟆,不要執(zhí)行相應(yīng)的函數(shù)。就當(dāng)作一個普通的點(diǎn)擊事件堂污。


  • 只要計時器在我們預(yù)設(shè)的時間內(nèi)沒有被清除家肯,即 mouseup 事件沒有被觸發(fā)——那么可以斷定用戶沒有釋放按鈕。因此盟猖,可以判定為一次長按息楔,可以執(zhí)行關(guān)聯(lián)的函數(shù)。


    實(shí)踐


    讓我們深入代碼扒披,完成這一功能值依。


    首先,我們必須定義三件事碟案,即:


  • 一個 變量 用于存儲計時器愿险。

  • 一個 啟動 功能函數(shù),用于啟動計時器价说。

  • 一個 取消 功能函數(shù),用于取消計時器鳖目。


  • 變量


    這個變量主要用來保存 setTimeout 的值领迈,以便當(dāng)鼠標(biāo) mouseup 事件觸發(fā)時我們可以取消它彻磁。


    let pressTimer = null;


    我們把變量值設(shè)置為 null 是為了在執(zhí)行取消操作前,檢查這個變量的值判斷當(dāng)前是否有一個正在運(yùn)行的計時器狸捅。


    啟動函數(shù)


    這個函數(shù)包括一個 setTimeout,它是 JavaScript 中的一個基本方法磁浇,允許在特定時間之后執(zhí)行一個函數(shù)。


    注意朽褪,click 事件執(zhí)行的過程中置吓,會觸發(fā)另外兩個事件无虚。但是我們需要啟動計時器的是 mousedown 事件友题。如果只是點(diǎn)擊事件构拳,不需要啟動計時器梁棠。


    // 創(chuàng)建計時器 ( 1s之后執(zhí)行函數(shù) )

    let start = (e) => {

    ????// 如果是點(diǎn)擊事件,不啟動計時器

    ????if (e.type === 'click' && e.button !== 0) {

    ????????return;

    ????}

    ????// 在啟動一個定時器之前確保沒有正在運(yùn)行的計時器

    ????if (pressTimer === null) {

    ????????pressTimer = setTimeout(() => {

    ????????????// 執(zhí)行任務(wù) !!!

    ????????}, 1000)

    ????}

    }


    取消函數(shù)


    這個函數(shù)見名知意凫海,用來取消啟動函數(shù)創(chuàng)建的 setTimeout男娄。


    要取消 setTimeout ,可以使用 JavaScript 中的 clearTimeout 方法建瘫,它主要用來清除 setTimeout() 方法設(shè)置的計時器尸折。


    在使用 clearTimeout 之前,需要檢查 pressTimer 變量是否為 null橄浓。如果沒有為 null亮航,意味著有一個正在運(yùn)行的計時器。因此准给,我們需要先清除它重抖,并且將 pressTimer 變量設(shè)置為 null。


    let cancel = (e) => {

    ????// 檢查 pressTimer 的值是否為 null

    ????if (pressTimer !== null) {

    ????????clearTimeout(pressTimer)

    ????????pressTimer = null

    ????}

    }


    一旦 mouseup 事件觸發(fā)沦辙,這個函數(shù)就會被調(diào)用讹剔。


    設(shè)置觸發(fā)器


    剩下的就是將事件監(jiān)聽器添加到想要長按效果的按鈕上。


    addEventListener("mousedown", start);

    addEventListener("click", cancel);


    以上代碼合到一起是這樣:


    // 定義變量

    let pressTimer = null;


    // 創(chuàng)建計時器( 1秒后執(zhí)行函數(shù) )

    let start = (e) => {


    ????if (e.type === 'click' && e.button !== 0) {

    ????????return;

    ????}


    ????if (pressTimer === null) {

    ????????pressTimer = setTimeout(() => {


    ????????????// 執(zhí)行任務(wù) !!!


    ????????}, 1000)

    ????}

    }


    // 停止計時器

    let cancel = (e) => {


    ????// 檢查是否有正在運(yùn)行的計時器

    ????if ( pressTimer !== null ) {

    ????????clearTimeout(pressTimer);

    ????????pressTimer = null;

    ????}

    }


    // 選擇 id 為 longPressButton 的元素

    let el = document.getElementById('longPressButton');


    // 添加事件監(jiān)聽器

    el.addEventListener("mousedown", start);


    // 長按事件取消沈跨,取消計時器

    el.addEventListener("click", cancel);

    el.addEventListener("mouseout", cancel);


    用 Vue 指令包裝


    創(chuàng)建 Vue 指令時兔综,可以創(chuàng)建全局或局部指令,本文中涧窒,我們采用全局指令锭亏。


    首先,我們必須聲明自定義指令的名稱戴已。


    Vue.directive('longpress', {


    })


    這就注冊了一個名為 v-longpress 的全局自定義指令锅减。


    接下來怔匣,我們添加帶參數(shù)的 bind 鉤子函數(shù),它允許我們引用指令綁定的元素劫狠,獲取傳遞給指令的值独泞,并標(biāo)識指令使用的組件。


    Vue.directive('longpress', {

    ????bind: function(el, binding, vNode) {


    ????}

    })


    接下來蜒犯,我們在 bind 函數(shù)中添加長按功能的代碼荞膘。


    Vue.directive('longpress', {

    ????bind: function(el, binding, vNode) {


    ????????// 定義變量

    ????????let pressTimer = null;


    ????????// 定義函數(shù)處理程序

    ????????// 創(chuàng)建計時器( 1秒后執(zhí)行函數(shù) )

    ????????let start = (e) => {


    ????????????if (e.type === 'click' && e.button !== 0) {

    ????????????????return;

    ????????????}


    ????????????if (pressTimer === null) {

    ????????????????pressTimer = setTimeout(() => {


    ????????????????????// 執(zhí)行任務(wù) !!!


    ????????????????}, 1000)

    ????????????}

    ????????}


    ????????// 取消計時器

    ????????let cancel = (e) => {


    ????????????// 檢查是否有正在運(yùn)行的計時器

    ????????????if ( pressTimer !== null ) {

    ????????????????clearTimeout(pressTimer);

    ????????????????pressTimer = null;

    ????????????}

    ????????}


    ????????// 添加事件監(jiān)聽器

    ????????el.addEventListener("mousedown", start);


    ????????// 取消計時器

    ????????el.addEventListener("click", cancel);

    ????????el.addEventListener("mouseout", cancel);

    ????}

    })


    接下來羽资,我們需要添加一個函數(shù)來運(yùn)行傳遞給 longpress 指令的方法。


    Vue.directive('longpress', {

    ????bind: function(el, binding, vNode) {


    ????????// 定義變量

    ????????let pressTimer = null;


    ????????// 定義函數(shù)處理程序

    ????????// 創(chuàng)建計時器( 1秒后執(zhí)行函數(shù) )

    ????????let start = (e) => {


    ????????????if (e.type === 'click' && e.button !== 0) {

    ????????????????return;

    ????????????}


    ????????????if (pressTimer === null) {

    ????????????????pressTimer = setTimeout(() => {

    ????????????????????// 執(zhí)行函數(shù)

    ????????????????????handler();

    ????????????????}, 1000)

    ????????????}

    ????????}


    ????????// 停止計時器

    ????????let cancel = (e) => {


    ????????????// 檢查是否有正在運(yùn)行的計時器

    ????????????if ( pressTimer !== null ) {

    ????????????????clearTimeout(pressTimer);

    ????????????????pressTimer = null;

    ????????????}

    ????????}


    ????????// 運(yùn)行函數(shù)

    ????????const handler = (e) => {

    ????????????// 執(zhí)行傳遞給指令的方法

    ????????????binding.value(e)

    ????????}


    ????????// 添加事件監(jiān)聽器

    ????????el.addEventListener("mousedown", start);


    ????????// 取消計時器

    ????????el.addEventListener("click", cancel);

    ????????el.addEventListener("mouseout", cancel);

    ????}

    })


    現(xiàn)在狭郑,可以在 Vue 應(yīng)用中使用這個指令了汇在,除非使用者給指令傳入的值不是一個函數(shù)。因此亩鬼,我們需要通過警告反饋給使用者阿蝶。


    為了反饋給使用者,我們在 bind 函數(shù)中添加了以下內(nèi)容:


    // 確保提供的表達(dá)式是函數(shù)

    if (typeof binding.value !== 'function') {

    ????// 獲取組件名稱

    ????const compName = vNode.context.name;

    ????// 將警告?zhèn)鬟f給控制臺

    ????let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be `;

    ????if (compName) { warn += `Found in component '${compName}' ` }


    ????console.warn(warn);

    }


    最后魄缚,如果這個指令也適用于觸屏設(shè)備焚廊,那會是極好的习劫。因此诽里,我們添加了 touchstart、touchend 和 touchcancel 事件監(jiān)聽器谤狡。


    最終代碼如下:


    Vue.directive('longpress', {

    ????bind: function(el, binding, vNode) {


    ????????// 確保提供的表達(dá)式是函數(shù)

    ????????if (typeof binding.value !== 'function') {

    ????????????// 獲取組件名稱

    ????????????const compName = vNode.context.name;

    ????????????// 將警告?zhèn)鬟f給控制臺

    ????????????let warn = `[longpress:] provided expression '${binding.expression}' is not a function, but has to be `;

    ????????????if (compName) { warn += `Found in component '${compName}' `}


    ????????????console.warn(warn);

    ????????}


    ????????// 定義變量

    ????????let pressTimer = null;


    ????????// 定義函數(shù)處理程序

    ????????// 創(chuàng)建計時器( 1秒后執(zhí)行函數(shù) )

    ????????let start = (e) => {


    ????????????if (e.type === 'click' && e.button !== 0) {

    ????????????????return;

    ????????????}


    ????????????if (pressTimer === null) {

    ????????????????pressTimer = setTimeout(() => {

    ????????????????????// 執(zhí)行函數(shù)

    ????????????????????handler();

    ????????????????}, 1000)

    ????????????}

    ????????}


    ????????// 取消計時器

    ????????let cancel = (e) => {


    ????????????// 檢查計時器是否有值

    ????????????if ( pressTimer !== null ) {

    ????????????????clearTimeout(pressTimer);

    ????????????????pressTimer = null;

    ????????????}

    ????????}


    ????????// 運(yùn)行函數(shù)

    ????????const handler = (e) => {

    ????????????// 執(zhí)行傳遞給指令的方法

    ????????????binding.value(e)

    ????????}


    ????????// 添加事件監(jiān)聽器

    ????????el.addEventListener("mousedown", start);

    ????????el.addEventListener("touchstart", start);


    ????????// 取消計時器

    ????????el.addEventListener("click", cancel);

    ????????el.addEventListener("mouseout", cancel);

    ????????el.addEventListener("touchend", cancel);

    ????????el.addEventListener("touchcancel", cancel);

    ????}

    })


    現(xiàn)在可以在 Vue 組件里使用了:


    <template>

    ????<div>

    ????????<button v-longpress="incrementPlusTen" @click="incrementPlusOne">{{value}}</button>

    ????</div>

    </template>


    <script>

    export default {

    ????data() {

    ????????return {

    ????????????value: 10

    ????????}

    ????},

    ????methods: {

    ????????// 增加1

    ????????incrementPlusOne() {

    ????????????this.value++

    ????????},

    ????????// 增加10

    ????????incrementPlusTen() {

    ????????????this.value += 10

    ????????}

    ????}

    }

    </script>


    . . .


    如果你想知道更多關(guān)于 自定義指令焰宣、可用的 鉤子函數(shù)匕积、可以傳遞到這個鉤子函數(shù)中的 參數(shù)榜跌、函數(shù)簡寫 的信息, 參照 @vuejs 官方文檔,作者做了很好的解釋悄蕾。

    最后編輯于
    ?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
    • 序言:七十年代末础浮,一起剝皮案震驚了整個濱河市库物,隨后出現(xiàn)的幾起案子戚揭,更是在濱河造成了極大的恐慌撵枢,老刑警劉巖,帶你破解...
      沈念sama閱讀 206,482評論 6 481
    • 序言:濱河連續(xù)發(fā)生了三起死亡事件潜必,死亡現(xiàn)場離奇詭異沃但,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)垂攘,發(fā)現(xiàn)死者居然都...
      沈念sama閱讀 88,377評論 2 382
    • 文/潘曉璐 我一進(jìn)店門晒他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逸贾,“玉大人,你說我怎么就攤上這事铝侵∵湎剩” “怎么了?”我有些...
      開封第一講書人閱讀 152,762評論 0 342
    • 文/不壞的土叔 我叫張陵猾警,是天一觀的道長隆敢。 經(jīng)常有香客問我,道長穴墅,這世上最難降的妖魔是什么? 我笑而不...
      開封第一講書人閱讀 55,273評論 1 279
    • 正文 為了忘掉前任皇钞,我火速辦了婚禮松捉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘可柿。我一直安慰自己丙者,他們只是感情好,可當(dāng)我...
      茶點(diǎn)故事閱讀 64,289評論 5 373
    • 文/花漫 我一把揭開白布目锭。 她就那樣靜靜地躺著痢虹,像睡著了一般兰绣。 火紅的嫁衣襯著肌膚如雪编振。 梳的紋絲不亂的頭發(fā)上,一...
      開封第一講書人閱讀 49,046評論 1 285
    • 那天臀玄,我揣著相機(jī)與錄音畅蹂,去河邊找鬼。 笑死累贤,一個胖子當(dāng)著我的面吹牛少漆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渗磅,決...
      沈念sama閱讀 38,351評論 3 400
    • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仔掸!你這毒婦竟也來了医清?” 一聲冷哼從身側(cè)響起,我...
      開封第一講書人閱讀 36,988評論 0 259
    • 序言:老撾萬榮一對情侶失蹤鞋怀,失蹤者是張志新(化名)和其女友劉穎密似,沒想到半個月后葫盼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
      沈念sama閱讀 43,476評論 1 300
    • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抛猫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
      茶點(diǎn)故事閱讀 35,948評論 2 324
    • 正文 我和宋清朗相戀三年闺金,在試婚紗的時候發(fā)現(xiàn)自己被綠了峰档。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
      茶點(diǎn)故事閱讀 38,064評論 1 333
    • 序言:一個原本活蹦亂跳的男人離奇死亡掀亩,死狀恐怖欢顷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炼七,我是刑警寧澤布持,帶...
      沈念sama閱讀 33,712評論 4 323
    • 正文 年R本政府宣布鳖链,位于F島的核電站墩莫,受9級特大地震影響逞敷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裂问,卻給世界環(huán)境...
      茶點(diǎn)故事閱讀 39,261評論 3 307
    • 文/蒙蒙 一牛柒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧椭更,春花似錦蛾魄、人聲如沸。這莊子的主人今日做“春日...
      開封第一講書人閱讀 30,264評論 0 19
    • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至主届,卻和暖如春嘹狞,著一層夾襖步出監(jiān)牢的瞬間誓竿,已是汗流浹背。 一陣腳步聲響...
      開封第一講書人閱讀 31,486評論 1 262
    • 我被黑心中介騙來泰國打工涧偷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毙死,地道東北人扼倘。 一個月前我還...
      沈念sama閱讀 45,511評論 2 354
    • 正文 我出身青樓除呵,卻偏偏與公主長得像爪喘,于是被迫代替她去往敵國和親秉剑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
      茶點(diǎn)故事閱讀 42,802評論 2 345

    推薦閱讀更多精彩內(nèi)容

    • 第一部分 HTML&CSS整理答案 1. 什么是HTML5诡曙? 答:HTML5是最新的HTML標(biāo)準(zhǔn)略水。 注意:講述HT...
      kismetajun閱讀 27,422評論 1 45
    • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容渊涝,還有我對于 Vue 1.0 印象不深的內(nèi)容。關(guān)于...
      云之外閱讀 5,045評論 0 29
    • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法炸卑,類相關(guān)的語法煤傍,內(nèi)部類的語法,繼承相關(guān)的語法五续,異常的語法龄恋,線程的語...
      子非魚_t_閱讀 31,581評論 18 399
    • 趁著天黑之前 趁著醉意綿綿 趁著酒溫茶熱 趁著遠(yuǎn)方還不遠(yuǎn) 我想聽聽你的故事 冒險或是孤獨(dú) 啤酒還有幸福 一絲絲就著...
      名字帶帥的糙漢閱讀 212評論 3 3
    • 書聲朗朗的校園透著青春靚麗的氣息郭毕,每一天的生活過得都是那么的安逸。歡聲笑語里一直有同學(xué)們的陪伴显押,語文課上女老師的香...
      元十三先生閱讀 236評論 0 4