11.Vue技術棧開發實戰-從SplitPane組件談Vue中如何“操作”DOM


本節代碼對照:

https://github.com/lison16/vue-cource/blob/master/src/components/split-pane/split-pane.vue

https://github.com/lison16/vue-cource/blob/master/src/views/split-pane.vue

 

vue中是 數據驅動視圖
 

創建組件的文件夾叫做split-pane

在路由列表里面注冊一下

添加這個vue頁面

創建組件的vue文件,設置name為SplitPane

創建index.js.把組件引進來,並導出去

引入這個組件,然后注冊這個組件。

容器中分成兩個區域,通過拖動來改變兩個區域的比例。

還可以上下,嵌套使用。



分為三個部分,左邊的區域,右邊的區域,然后是中間可拖動的部分

我們首先來定義三個div。它的寬和高應該和父容器是一樣的,所以這里我們設置它的高度和寬度是100%

寬度其實可以不設置,div是一個塊級元素,它本身的寬度就是和它容器的寬度是一樣的

less里面的寫法,有層級管理的關系的class類型,可以嵌套着寫。他們有一個共同的class是pane。左邊是pane-left右邊是pane-right

它們的高度也是和父容器一樣高

&-left就是拼接了父級別的pane。所以這里就相當於是.pane-left

我們給左邊加了一個背景色,可以看到它的顏色和父容器是一樣寬的。

再給右側的div設置一個背景色是橘黃色的,可以看到倆div成了上下布局了,因為他倆的寬度都是100%和父容器的寬度一樣。所以右側的就被擠下來了

想讓這倆div在一排上顯示。給.pane設置寬度為50%。值是寬度變窄了, 還沒有在一排上顯示。

可能有個默認的樣式是居中的,

src根目錄下的 App.vue 把這里注釋掉。因為設置了居中顯示,所以導致了上面的問題。


這里改成float:left。這樣就上到一行里面顯示了。

如果想設置左邊是30%的寬度,右邊是70%的寬度
把float:left和width都去掉。

給他倆一個position,都設置為絕對定位

設置絕對定位后,它會往上找,去他的父元素一級一級的找,它會根據有定位屬性的父元素來定位。如果沒有它就一直找到html標簽。

所以這里地方我們要給wrapper一個相對定位的屬性。這樣又不會使wrapper的元素受到影響。 

讓他倆的父元素的top位置都是0,左邊設置寬度為30%。右邊這個呢?也想讓他用30%的這個值,top:0我們在.pane已經設置了,這里會繼承過來,


這樣設置右邊就是70%的寬度,用到了同樣的變量30%。這樣就是兩邊加起來就是100%

把左邊改成20%

右邊的left也改成20%。這樣他倆加起來就是100%

30%的這個值,想通過js變量去控制,這樣這個值就不能寫在css里面了。我們給它綁定一個變量,變量在data里面定義。

30%通過計算屬性計算出來。

這樣就把左邊的30%的css屬性注釋掉。

右邊是通過left的值,修改它的寬度。所以右邊我們綁定的css屬性是left




通過一個按鈕改變這個變量的值來試一下

把這個按鈕放在了左側的面板里面


通過點擊左側按鈕的面板,改變左側pane的寬度

每次點擊寬度變小。Vue里面,這樣通過數據驅動樣式的改變,這樣避免了數據和視圖不對應

定義trigger中間拖動條




它要顯示在中間也是需要設置絕對定位。設置了top為0,它顯示在了最左側。

也是給中間的條綁定style屬性。但是綁定上left后,視圖中紅色的調不見了。

中間的條,實際上在這里,說明可能是被元素遮住了。

通過設置一個z-index來看下

紅色的條 實際上是蓋在,右側的

我們希望他在最中間顯示。
首先中間條的寬度我們通過屬性傳遞進來,默認是8.我們定義個props屬性 triggerWidth

首先是向左偏移30%和left的寬度是一樣的,然后它需要減少4個像素,來達到在最中間,這里我們用到css3的一個方法calc(),它允許你在里面做一些計算。

30%的偏移再減去4像素,就可以這么寫

減去的是triggerWidth除以2


return `calc(${this.leftOffset * 100}% - ${this.triggerWidth / 2}px)`

 




這里綁定的是 triggerWidh 后面是px

 

<div class="pane pane-trigger-con" :style="{ left: triggerLeft, width: `${triggerWidth}px` }"></div>

 



中間只用到了width屬性

說明我們這里的計算屬性並沒有生效

我們這里把計算出來的值打印出來看一下


這里的單詞拼錯, 應該是calc

紅色的條被左側遮住了一半

被右側也遮住了一半

鼠標拖動中間trigger條

這就用到了鼠標的三個事件了
首先綁定一個mousemove事件

我們會用到參數里面的pageX就是鼠標距離頁面左側的像素,

只要能算出來鼠標距離左側有多少像素,我們只要知道這個容器距離頁面左側多少,用鼠標的距離減去容器的距離頁面的距離,就是左側left的寬度了。

獲取dom元素用ref


那么怎么獲取父容器距離左邊的距離呢?獲取dom容器呢 我們用ref屬性,給父容器div加一個ref屬性為outer

getBoundingClientRect

理解:getBoundingClientRect用於獲取某個元素相對於視窗的位置集合。集合中有top, right, bottom, left等屬性。

2.返回值類型:
 rectObject.top:元素上邊到視窗上邊的距離;

rectObject.right:元素右邊到視窗左邊的距離;

rectObject.bottom:元素下邊到視窗上邊的距離;

rectObject.left:元素左邊到視窗左邊的距離;

參考簡書: https://www.jianshu.com/p/824eb6f9dda4
 
 


獲取到dom后,它有個方法叫做getBoundingClientRect,這個方法返回一個對象,

這個方法返回一個對象,對象里面返回一個屬性,

先來看下它返回的值

對象里面包含了這些屬性,這些屬性在IE9下也是支持的,所以兼容性是沒有問題的。

所以這里我們減去left


鼠標的事件,有時候鼠標會超過這個框,應該綁定在頁面級別。

所以我們把mouseMove事件綁定子在document上。就是鼠標在中間的條上,按下的時候綁定這個事件。
所以這里中間的條的事件,改成mousedown事件

鼠標按下我們給document綁定一個事件,它的回調事件是下面的handleMousemove事件


當點擊這個條的時候,然后鼠標在頁面上移動,console內不斷的輸出的

偏移量除以容器的總寬度,再乘以100,就是百分比。

outerRect.width是容器的寬度,也就是dom對象的總的寬度

乘以100 去掉

this.leftOffset = (offset / outerRect.width)

 


現在這個條就隨着鼠標的移動而移動了

我們希望按下鼠標的時候 開始移動,松開鼠標的時候就應該停止了。
所以我們就需要有個狀態,鼠標是按下還是抬起

只有為true的時候才能移動。

mouseup的事件

這樣只有鼠標按下才能移動,鼠標抬起就不能移動了。

點擊中間的條,靠左半邊點,或者靠右半邊的時候點擊會出現這個偏移量的問題。
在鼠標按下的時候,有一個瞬間的偏移。出現問題的原因是我們在注冊事件的時候忽略了 中間的條 也有個寬度。

所以在條上有個初始的偏移量,它的初始是0


我們就要給這個偏移量設置一個值,當我們按下的時候。我要把這個寬度也計算在內。

減去這個條距離頁面左側的距離。就是鼠標距離這個條的左側距離。

因為這個事件是發生在這個條上的,

this.initOffset = event.pageX - event.srcElement.getBoundingClientRect().left

 



在move的時候,我們要減去這個值

還需要再加上條一半的寬度

這個時候再去拖動,就不會有一瞬間的移動了

設置最大值和最小值。

拖動最右邊應該有個頭,這里完全拖出去了。

設置最小的百分比值,是0.1

當你的拖動到百分之0.1的時候,左側的值就不會再變了。



除法的邏輯加到上面去

如果偏移量小於這個最小值的話,那么就用這個最小值。


到了最小值 再拖,就拖不動了。

我們再來設置一個最大值,最大值設置為0.9

超過最大值,就是最大值。



拖動過程中,選中的問題

拖動過程中,偶爾會做了一些選中的操作


通過設置一個樣式。這樣他就不會在這個元素上選中操作了。

初始偏移量

設置初始的偏移

調用的時候就可以設置初始的偏移了 。

這里就注釋掉,不用了。

這里就改成value的形式

我們是通過拖動來修改這個值的,那么有個問題就是。子組件如果想改變父組件的值,不能直接通過這種賦值的形式。還是要通過事件來觸發 ,告訴父組件,父組件內做事件的響應,

再父組件內,把這個傳入百分比值改成變量

然后給它綁定一個事件

回調函數里面有個參數,我們接收這個參數,並賦值給偏移量的變量值



在這里我們要拋出這個事件

拖動和修改都沒有問題了。

 

<split-pane :value="offset" @input="handleInput"></split-pane>

 

 

另外一種方式vue提供的語法糖

v-model簡寫的形式



.sync修飾符


方法注釋掉

子組件內,事件也不拋出去了。改用update:value



這么寫首先給value綁定值offset,同時會給你綁定一個事件會更新這個offset的值,

子組件內要觸發一個事件,固定的就叫做update。

update后面的:value就是我們這里父組件的屬性名

最后 就是你要更新的值

在區域內填充內容

就用到了我們之前講到的slot插槽的概念



右邊的right給壓到中間的條下面了。給右側綁定一個triggerWidth除以2
加一個paddingLeft的屬性 是中間調的寬度除以2


這樣它的內容就不會被壓到了。


同理left也有一個paddingRight屬性

鼠標的樣式

鼠標放上去,要讓用戶知道這是一個可以拖動的,我們就需要給它設置一個css的演示
鼠標的樣式記不住呢,推薦大家一個網站
http://css-cursor.techstream.org/


鼠標放上去會顯示對應的鼠標樣式。


左右拖動的效果就是這個了



 

結束


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM