用 Vue 編寫一個長按指令

最近學習了一下vue.js。對這一框架的最深入印象就是vue的組件化思想和數據驅動視圖而不直接操做DOM的思想。在JQuery逐漸黯淡的前端發展趨勢下,這種UI狀態的變化操做方式勢必會成爲主流。那麼在此以一個小例子做爲說明。前端

建立一個按鈕,按下一次就能夠清除單次輸入(或者持續按住能夠清除全部輸入),即按下(或者按住)一個按鈕時,既執行一個函數,又清除輸入。
vue

首先會始用JS實現,再用vue實現,以便對比突出vue特色express

原理框架

要實現長按,用戶須要按下並按住按鈕幾秒鐘。ide

想經過代碼模擬這一效果,咱們須要在鼠標「點擊」按下按鈕時,啓動一個計時器監聽用戶按下的時長,若是時間超過咱們指望的時長,就執行相應的函數。函數

很是簡單!然而,咱們須要知道用戶什麼時候按住按鈕。
組件化

如何實現
學習

當用戶點擊按鈕時,在點擊事件以前會觸發另外兩個事件: mousedown 和 mouseup。
this

當用戶按下按鈕時觸發 mousedown 事件,用戶鬆開按鈕時調用 mouseup 事件。
component

咱們須要作的是:

  1. mousedown 事件觸發時,啓動計時器。

  2. 一旦 mouseup 事件在預期的 2 秒前被觸發,就清除計時器,不要執行相應的函數。就看成一個普通的點擊事件。

只要計時器在咱們預設的時間內沒有被清除,即 mouseup 事件沒有被觸發——那麼能夠判定用戶沒有釋放按鈕。所以,能夠斷定爲一次長按,能夠執行關聯的函數。

實踐

讓咱們深刻代碼,完成這一功能。

首先,咱們必須定義三件事,即:

  1. 一個 變量 用於存儲計時器。

  2. 一個 啓動 功能函數,用於啓動計時器。

  3. 一個 取消 功能函數,用於取消計時器。

變量

這個變量主要用來保存 setTimeout 的值,以便當鼠標 mouseup 事件觸發時咱們能夠取消它。

let pressTimer = null;


咱們把變量值設置爲 null 是爲了在執行取消操做前,檢查這個變量的值判斷當前是否有一個正在運行的計時器。

啓動函數

這個函數包括一個 setTimeout,它是 JavaScript 中的一個基本方法,容許在特定時間以後執行一個函數。

注意,click 事件執行的過程當中,會觸發另外兩個事件。可是咱們須要啓動計時器的是 mousedown 事件。若是隻是點擊事件,不須要啓動計時器。

// 建立計時器 ( 1s以後執行函數 )

let start = (e) => {

// 若是是點擊事件,不啓動計時器

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

return;

}

// 在啓動一個定時器以前確保沒有正在運行的計時器

if (pressTimer === null) {

pressTimer = setTimeout(() => {

// 執行任務 !!!

}, 1000)

}

}


取消函數

這個函數見名知意,用來取消啓動函數建立的 setTimeout。

要取消 setTimeout ,能夠使用 JavaScript 中的 clearTimeout 方法,它主要用來清除 setTimeout() 方法設置的計時器。

在使用 clearTimeout 以前,須要檢查 pressTimer 變量是否爲 null。若是沒有爲 null,意味着有一個正在運行的計時器。所以,咱們須要先清除它,而且將 pressTimer 變量設置爲 null。

let cancel = (e) => {

// 檢查 pressTimer 的值是否爲 null

if (pressTimer !== null) {

clearTimeout(pressTimer)

pressTimer = null

}

}

一旦 mouseup 事件觸發,這個函數就會被調用。

設置觸發器

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

addEventListener("mousedown", start);

addEventListener("click", cancel);

以上代碼合到一塊兒是這樣:

// 定義變量

let pressTimer = null;

// 建立計時器( 1秒後執行函數 )

let start = (e) => {

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

return;

}

if (pressTimer === null) {

pressTimer = setTimeout(() => {

// 執行任務 !!!

}, 1000)

}

}

// 中止計時器

let cancel = (e) => {

// 檢查是否有正在運行的計時器

if ( pressTimer !== null ) {

clearTimeout(pressTimer);

pressTimer = null;

}

}

// 選擇 id 爲 longPressButton 的元素

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

// 添加事件監聽器

el.addEventListener("mousedown", start);

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

el.addEventListener("click", cancel);

el.addEventListener("mouseout", cancel);

用 Vue 指令包裝

建立 Vue 指令時,能夠建立全局或局部指令,本文中,咱們採用全局指令。

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

Vue.directive('longpress', {

})

這就註冊了一個名爲 v-longpress 的全局自定義指令。

接下來,咱們添加帶參數的 bind 鉤子函數,它容許咱們引用指令綁定的元素,獲取傳遞給指令的值,並標識指令使用的組件。

Vue.directive('longpress', {

bind: function(el, binding, vNode) {

}

})

接下來,咱們在 bind 函數中添加長按功能的代碼。

Vue.directive('longpress', {

bind: function(el, binding, vNode) {

// 定義變量

let pressTimer = null;

// 定義函數處理程序

// 建立計時器( 1秒後執行函數 )

let start = (e) => {

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

return;

}

if (pressTimer === null) {

pressTimer = setTimeout(() => {

// 執行任務 !!!

}, 1000)

}

}

// 取消計時器

let cancel = (e) => {

// 檢查是否有正在運行的計時器

if ( pressTimer !== null ) {

clearTimeout(pressTimer);

pressTimer = null;

}

}

// 添加事件監聽器

el.addEventListener("mousedown", start);

// 取消計時器

el.addEventListener("click", cancel);

el.addEventListener("mouseout", cancel);

}

})

接下來,咱們須要添加一個函數來運行傳遞給 longpress 指令的方法。

Vue.directive('longpress', {

bind: function(el, binding, vNode) {

// 定義變量

let pressTimer = null;

// 定義函數處理程序

// 建立計時器( 1秒後執行函數 )

let start = (e) => {

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

return;

}

if (pressTimer === null) {

pressTimer = setTimeout(() => {

// 執行函數

handler();

}, 1000)

}

}

// 中止計時器

let cancel = (e) => {

// 檢查是否有正在運行的計時器

if ( pressTimer !== null ) {

clearTimeout(pressTimer);

pressTimer = null;

}

}

// 運行函數

const handler = (e) => {

// 執行傳遞給指令的方法

binding.value(e)

}

// 添加事件監聽器

el.addEventListener("mousedown", start);

// 取消計時器

el.addEventListener("click", cancel);

el.addEventListener("mouseout", cancel);

}

})

如今,能夠在 Vue 應用中使用這個指令了,除非使用者給指令傳入的值不是一個函數。所以,咱們須要經過警告反饋給使用者。

爲了反饋給使用者,咱們在 bind 函數中添加了如下內容:

// 確保提供的表達式是函數

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

// 獲取組件名稱

const compName = vNode.context.name;

// 將警告傳遞給控制檯

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);

}

最後,若是這個指令也適用於觸屏設備,那會是極好的。所以,咱們添加了 touchstart、touchend 和 touchcancel 事件監聽器。

最終代碼以下:

Vue.directive('longpress', {

bind: function(el, binding, vNode) {

// 確保提供的表達式是函數

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

// 獲取組件名稱

const compName = vNode.context.name;

// 將警告傳遞給控制檯

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

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

console.warn(warn);

}

// 定義變量

let pressTimer = null;

// 定義函數處理程序

// 建立計時器( 1秒後執行函數 )

let start = (e) => {

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

return;

}

if (pressTimer === null) {

pressTimer = setTimeout(() => {

// 執行函數

handler();

}, 1000)

}

}

// 取消計時器

let cancel = (e) => {

// 檢查計時器是否有值

if ( pressTimer !== null ) {

clearTimeout(pressTimer);

pressTimer = null;

}

}

// 運行函數

const handler = (e) => {

// 執行傳遞給指令的方法

binding.value(e)

}

// 添加事件監聽器

el.addEventListener("mousedown", start);

el.addEventListener("touchstart", start);

// 取消計時器

el.addEventListener("click", cancel);

el.addEventListener("mouseout", cancel);

el.addEventListener("touchend", cancel);

el.addEventListener("touchcancel", cancel);

}

})

如今能夠在 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>

相關文章
相關標籤/搜索