svg 编辑器的点击事件兼容pc端和移动端方案
本人開發(fā)的svg編輯器原本只是針對 PC 端的。在pc端瀏覽器,可以正常地進行矢量圖形地繪制,但在移動端無法使用,在畫布上點擊沒有什么效果。但后來希望可以在一些屏幕大的移動設備上也能簡單的使用,做了一些兼容方案,于是總結寫下了這篇點擊事件的pc端和移動端的適配處理方案分享。
基本架構
和 Adobe Illustrator 一樣,這款 svg 編輯器也提供了很多工具。點擊工具圖標,就切換到對應的模式。
svg 編輯器目前支持下面工具:
- 選擇工具
- 路徑編輯工具
- 鉛筆工具
- 鋼筆(路徑)工具
- 添加錨點工具
- 刪除錨點工具
- 錨點工具
每個工具對應一個 action 對象。這個 action 對象有下面的屬性(為了簡明,這里我寫成 ts 的接口形式,不過我 ts 不太熟,下面的語法可能會有問題,看懂就行):
interface EventHandler {(e: object): void; }interface Action {name: string; // action 的名稱,如select,path等bindEvents: string[]; // 字符串數(shù)組,記錄需要綁定的事件名。如 mousedownunbind: function // 解綁事件鉤子mousedown ?: EventHandler // 事件名:事件響應函數(shù)。... } 復制代碼舉個例子:
let aciton: Action = {name: 'select',bindEvents: ['mousedown', 'mousemove', 'mouseup'],unbind(){ /* 銷毀在 select 模式下才會存在的對象 */}mousedown(e) { /* 鼠標按下事件響應函數(shù) */ },mousemove(e) {},mouseup(e) {}, } 復制代碼我們用一個名為 actionsManager.js 的文件來管理這些 action,來進行工具的切換。具體代碼就不說了,簡單說下它做了什么東西:
首先,我們會將這些 action 導入(即import)到該模塊下,然后保存到一個 actions 對象中。名為 bindEvents 的函數(shù)負責 action 的切換。為了實現(xiàn)切換功能,我們還需要一個 curAction 變量,來指向當前正在使用的 action。
這樣我們點擊切換的時候,就可以通過遍歷當前 action 的 bindEvents 數(shù)組的對應的所有事件響應函數(shù),通過 removeEventListener 方法一一取消綁定。然后我們再遍歷新的 action 的 bindEvents 數(shù)據(jù),使用 addEventListener 來 綁定事件響應函數(shù)。
自此,編輯器的工具切換的實現(xiàn)大致說完。下面開始說明如何適配移動端的點擊事件
移動端點擊事件適配
在對代碼進行改造前,我們先來了解一下移動端和pc端的點擊行為的異同。移動端有獨有的 touch 事件,雖然移動端是可以觸發(fā) mouse 事件,但和 pc 端有點不同。
PC端的點擊行為
pc端是鼠標按下觸發(fā) mousedown 事件。移動鼠標的時候觸發(fā) mouseover 事件,但移動過程中,一旦光標離開了 event.srcElement,就無法觸發(fā) mouseover 事件。鼠標釋放則觸發(fā) mouseup 事件。
移動端的點擊行為
手指按下時,會觸發(fā) touchstart 事件。此時手指不抬起進行移動的話,就會觸發(fā) touchmove 事件,即便移動到 event.srcElement 的范圍外,依舊會觸發(fā) touchmove 事件,此時的 srcElemnt 依舊是原來的節(jié)點,而不是當前手指位置對應的節(jié)點。手指抬起時,不管此時手指在哪里,也和 touchmove 事件一樣,srcElement 不會指向手指位置的節(jié)點。
移動端事件觸發(fā)順序
(1)移動端手指按下并提起(沒有較大位移),依次觸發(fā)以下事件:
(2)移動端手指按下并大幅移動,然后提起,觸發(fā)的事件順序:
這里我們會看到,touchmove 事件的觸發(fā)會導致 mouse 事件無法觸發(fā)。
事實上,只要給 touchstart 或 touchend 事件添加一行 event.preventDefault(),當發(fā)生 touch 行為時,mouse 事件就無法觸發(fā)。touchmove 默認就能阻止 mouse 事件的觸發(fā),但在發(fā)生 touch 行為中,它不一定會觸發(fā)(需要有較大偏移量)。
touchEvent
touchEvent 對象,是 touch 事件觸發(fā)時產(chǎn)生的事件對象。它和 mouse 事件對象有一些不同的地方。首先手指的當前位置,是存放在一個名為 touches 的數(shù)組里的。該數(shù)組保存著 touch 對象。touch 對象有如 clentX, clientY 等很多坐標相關的屬性,唯獨沒有 offsetX/offsetY 屬性。
代碼改造
下面我們就來對原有的代碼進行改造。
移動端的 offsetX/offsetY 計算
touch 事件并不提供 offsetX 和 offsetY 屬性。然而這兩個屬性對一款 svg 來說是必不可少的屬性。我們需要用到這兩個屬性,來定位光標在畫布上的位置,來繪制路徑等圖案。
為此我們需要寫一個方法,將 pageX/pageY(光標距離頁面左上角的位置)轉換為 offsetX/offsetY,并作為 e 的補充屬性。它們的關系是:
offsetX + left = pageX; offsetY + top = pageY; 復制代碼left(top) 表示綁定 touch 事件的元素,距離頁面左上角的距離。
const handleEventObj = function(e) {const LEFT = 256; // 需要根據(jù)實際情況進行計算。const TOP = 60;const touch = e.changedTouches[0];const offsetX = touch.pageX - LEFT + workarea.scrollLeft;const offsetY = touch.pageY - TOP + workarea.scrollTop;Object.assign(e, {offsetX, offsetY});return e; }復制代碼改造工具切換函數(shù)
方案1:判斷pc端還是移動端,決定是綁定 touch 事件還是 mouse 事件。
一開始我就是使用這種方案的,但它有一個問題。如果對于使用普通電腦的用戶來說,這是沒問題的,但如果用的是像 surface 這種可以觸屏的筆記本,那就會有問題。因為通過觸屏點擊后,會觸發(fā) touch 事件,但 mouse 事件不一定會觸發(fā)。即使觸發(fā)了 mouse 事件,也是通過抬起手指觸發(fā)的,而且是 mouseover -> mousedown -> mouseup 的順序,且 mouseover 在手指按下移動過程中是無法觸發(fā)的。
這種方案有很嚴重的問題,它無法勝任 觸屏筆記本 的情況。
方案2:同時綁定 touch 事件和 mouse 事件
具體做法是,在 actionsManager.js 引入所有 action 的時候,就自動遍歷所有 action 的 bindEvents。如果有 mousedown/mouseover/mouseup 事件響應函數(shù),就額外添加對應的 touchstart/touchmove/touchend 事件響應函數(shù)。
具體代碼如下:
const map = {mousedown: 'touchstart',mousemove: 'touchmove',mouseup: 'touchend', };if (['mousedown', 'mousemove', 'mouseup'].includes(eventName)) {action.bindEvents.push(map[eventName])action[map[eventName]] = function(e) {e.preventDefault(); // 這個很重要,它能阻止后續(xù)的 mouse 事件。handleEventObj(e); // event 對象添加 offsetX ofsetY 屬性。action[eventName](e);} } 復制代碼這里的 eventName 就是事件名。
這里舉個例子,假設我們有個 selectAction 對象,它有一個 mousedown 事件,我們就給 selectAction 添加一個 touchstart 方法。這個 touchstart 方法會阻止默認事件,這樣就能阻止 mouse 事件觸發(fā),然后給 event 對象添加 offsetX/offsetY,把 event 傳入 action.mousedown 方法。
這樣,在 ipad 等移動端配合觸屏筆,也可以進行簡單的 svg 編輯操作了。
參考
掘金——touchstart與click不得不說的故事
總結
以上是生活随笔為你收集整理的svg 编辑器的点击事件兼容pc端和移动端方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手眼标定(eye in hand)-步骤
- 下一篇: 恩智浦智能车赛。摄像头处理函数。