一個能拖動,能調整大小,能更新bind值的vue指令-vuedragx

1、 背景說明

開發一個可自定義組件化門戶配置頁面,期間採用了vue框架做爲前端視圖引擎,做爲一個剛入手vue的萌新,開發第一個功能就遇到了攔路虎。須要一個拖動而且可改變大小的容器盒子。當時查看vue開發文檔,查找github都沒找到一個本身歡心的實現,因此與其求人,還不如求己。因此vuedragx這個輪子就有了,x表明它不止可拖動。
github地址:github.com/464884492/v…javascript

2、效果圖

Alt text

3、開發思路

  • 經過鼠標移動實現組件移動,改變大小,必定須要操做dom,查看vue官方文檔,從實用性,已經通用性,選擇開發一個自定義vue指令
  • 經過鼠標移動產生的位移,動態改變大小或位置
  • 經過事件通知方式,實現更新bind值

因此有了以上思路,就須要一次掌握三個重要知識html

  1. vue 若是開發一個自定義指令
  2. 鼠標移動過程當中,MouseEvent對象各類值的含義
  3. 如何使用並分發一個自定義事件

3.1 vue指令

記住,無論使用什麼樣的框架,須要學習某種技能,須要學習api各類方法的用法,最好的途徑就是查看對應官方文檔前端

vue 開發自定義指令地址 cn.vuejs.org/v2/guide/cu…
通讀一篇,而後敲黑板畫重點vue

  1. 何時使用Vue指令

官方是麼說的 在 Vue2.0 中,代碼複用和抽象的主要形式是組件。然而,有的狀況下,你仍然須要對普通 DOM 元素進行底層操做,這時候就會用到自定義指令java

顯而易見,經過須要使用鼠標產生的位移,經過啥啥實在是比較爲難在下了node

  1. 如何開發

針對vue指令,官方給開發這提供了幾個勾子函數,來注入開發者開發的功能,所謂的鉤子函數,我的理解也就是在正常的代碼中插入預埋的未實現的函數接口git

vue對開發着提供(bind、inserted、update、componentUpdated、unbind)注入點。github

  • bind 只調用一次,指令第一次綁定到元素時調用,主要完成指令初始化設置
  • inserted 被綁定元素插入父節點時調用,官網對這裏有一個強調 僅保證父節點存在,但不必定被插入文檔中 我的認爲這個保證父節點,是隻存在與所謂的虛dom中,只是沒有實際應用與真實的dom中,不知道這個理解是否正確?
  • update 所在組件的VNode更新時調用,但也可能發生在其子VNode更新以前,因此提供的參數中包含 vnode oldVnode,具體是否須要響應操做,能夠比較這兩個node中對應的值是否變化
  • componentUpdated 指令所在組件的 VNode極其子VNode所有更新後調用,此鉤子函數的補充,就能夠解決update鉤子,不及時更新問題。
  • unbind 只調用一次,指令與元素解綁是調用

每個鉤子都帶有參數 el、binding、vnode、oldVnodeexpress

  • el 當前綁定的dom
  • binding 對象,包含如下屬性
    • name 指令名稱,不過我的理解此參數感受沒有使用的場景
    • value 指令bind的值,若是是可運行的表達式,將返回計算結果。若是綁定的是一個對象,將返回此對象,vuedragx,採用此屬性傳入個性化配置信息。
    • oldValue 指令綁定的前一個值,僅在 update 和 componentUpdated 鉤子函數中可用
    • expression 字符串形式的指令表達式,就是在代碼開發中v-dragx=」A」,這個A就是表達式
    • arg 傳給指令的參數,如 v-dragx:foo,參數爲foo,注意標識符 :
    • modifiers 一個包含修飾符的對象,如:v-dragx.dragable.resizeable,修飾符對象爲{dragable:true,resizeable:true},不過vuedragx並無使用此方法傳值,統一經過binding.value傳值
    • vnode Vue 編譯生成的虛擬節點
    • oldVnode 上一個虛擬節點,僅在 update 和 componentUpdated 鉤子中可用

關於vue指令的鉤子函數,參數等相關說明,基本從官網引用過來的。我以爲有必要再次在強調如下,使用第三方的框架,最好途徑就是查閱官方文檔。其次能夠看看對應的源碼,若是有的話。api

3.2 MouseEvent

上邊在vue指令介紹中,強調了查閱官方文檔的重要性,那麼針對javascript的api又該在那裏查閱呢?固然是 MDN,沒有第二

MDN中關於MouseEvent說明的地址: developer.mozilla.org/zh-CN/docs/…,開發vuedragx須要MouseEvent中最重要的兩個屬性MouseEvent.pageXMouseEvent.pageY

  • MouseEvent.pageX 相對於整個文檔的x(水平)座標。此屬性包含了元素滾動的距離 ,官方解釋爲 這個屬性將基於文檔的邊緣,考慮任何頁面的水平方向上的滾動。舉個例子,若是頁面向右滾動 200px 並出現了滾動條,這部分在窗口以外,而後鼠標點擊距離窗口左邊 100px 的位置,pageX 所返回的值將是 300。

作爲延伸,分別查看了MonuseEvent中其餘對應座標的屬性

  • MouseEvent.clientX 提供事件發生時的應用客戶端區域的水平座標 (與頁面座標PageX不一樣,clientX不包含滾動條的距離),因此pageX =clientX+橫向滾動的距離
  • MouseEvent.movementX 它提供了當前事件和上一個mousemove事件之間鼠標在水平方向上的移動值,此值能夠獲取每次鼠標移動的增量,在開發vuedargx沒有關注此屬性,否則可減小一部分計算代碼了。
  • MouseEvent.offsetX 規定了事件對象與目標節點的內填充邊(padding edge)在 X 軸方向上的偏移量
  • MouseEvent.screenX 供了鼠標相對於屏幕座標系的水平偏移量 不包含滾動調距離
  • MouseEvent.x 是 MouseEvent.clientX 屬性的別名.

因此,他們之間的關係應該是這樣的

Alt text
offsetX ,由於點擊的document,因此此時offsetx=clientx=x,movementX 很差畫圖,讀者就本身聯想了。

3.3 自定義事件

用過jQuery的小夥伴,應該多少了解 trigger``triggerHandler方法,觸發自定義事件,有了jq的幫助,自定義事件用的是那麼駕輕就熟,如絲版順滑。作爲對比,咱們先看看jq是如何使用自定義事件的

   
// 添加一個適當的事件監聽器
$( '#foo').on( 'custom', function(event, param1, param2) {
alert(param1 + "\n" + param2);
});
$( '#foo').trigger( 'custom', [ 'Custom', 'Event']);
複製代碼

對,就是這麼簡單,定義,傳參一鼓作氣!
不過用Vue後,這些功能都要用原生的代碼實現,業界也愈來愈強調原生代碼呼聲也愈來愈高。或許這句是es標準逐步完善,瀏覽器逐步兼容新api帶來的利好。這裏應該向IE6致敬(逃)。
言歸正傳,標準接口是如何使用自定義事件的呢?因而有找到了MDN developer.mozilla.org/zh-CN/docs/…,找到了demo

   
// 添加一個適當的事件監聽器
obj.addEventListener( "custom", function(e) {
console.log( JSON.stringify(e.detail));
})
// 建立並分發事件
var event = new CustomEvent( "custom", { "detail":{ "Custom": true}});
obj.dispatchEvent(event)
複製代碼

在我看來,定義事件的樣子,基本沒變,惟一參數傳遞變得複雜了。總有那麼一丟丟不美好。

4、實現

4.1 操做判斷

鼠標在目標對象上下左邊邊緣移動時,鼠標須要顯示不一樣的resize樣式,提醒用戶當前可操做的類型。基本思路是獲取當前對象的在文檔對應的left和top值,與上邊講解的MouseEvent中的 pageX、pageY加上可容忍的邊沿值作比較,分別分如下幾種狀況

  1. 左邊 left-edge<pageX<left+edge&& top+edge<pageY<top+height-edge
  2. 右邊 left+widht-edge<pageX<left+widht+edge&& top+edge<pageY<top+height-edge
  3. 上邊 top-edge<pageY<top+edge && left+edge<pageX<left+width-edge
  4. 下邊 top+heigth-edge<pageY<top+height+edge && left+edge<pageX<left+width-edge
  5. 左上角 top-edge<pageY<top+edge&&left-edge<pageX<left+edge
  6. 左下角 top+height-edge<pageY<top+height+edge&&left-edge<pageX<left+edge
  7. 右上角 top-edge<pageY<top+edge&&left+width-edge<pageX<left+width+edge
  8. 右下角 top+height-edge<pageY<top+height+edge&&left+width-edge<pageX<left+widht+edge

因此具體代碼是這樣的

Alt text
判斷當前鼠標是否在拖動對象上就沒有那麼費勁了,直接在onmousemove事件中經過
e.target.classList.contains(cfg.dragBarClass)判斷便可

4.2 調整大小

經過計算獲得鼠標移動的deltx和delty值,分別更新width和heigth屬性。當前在向左和向上調整大小,還須要調整對一個的left和height屬性值

4.3 拖動

拖動,不改變目標對象大小,直接用計算的deltx和delty更新對應的left和top屬性便可,若是須要限制移動區域,須要計算父容器對應內邊距的座標

   
function setConstraint(data) {
if (cfg.dragContainerId) {
let constraintDom = document.querySelector( "#" + cfg.dragContainerId);
let constraintRect = constraintDom.getBoundingClientRect();
if (data.left <= 0) data.left = 0;
if (data.top <= 0) data.top = 0;
if (data.top + data.height+data.borderTop+data.borderBottom >= constraintRect.height) data.top = constraintRect.height - data.height-data.borderTop-data.borderBottom;
if (data.left + data.width+data.borderLeft+data.borderRight > constraintRect.width) data.left = constraintRect.width - data.width-data.borderLeft-data.borderRight;
}
}
複製代碼

5、總結

雖然功能不大,但要把每個環節說清楚,感受仍是很費力。真羨慕那些會寫書的開發人員,文檔能力那是至關的好。寫文檔真的比不了寫代碼,不過仍是要寫,否則別人怎麼知道你代碼是作啥的。

相關文章
相關標籤/搜索