CSS實現可拉伸調整尺寸的分欄佈局

XboxYan發表於2022-06-20
歡迎關注我的公眾號:前端偵探

很多頁面佈局,特別是那種工具類的、比如編輯器、視覺化平臺等,為了充分的利用螢幕空間,都會提供拖拽調節各個分欄尺寸的功能,比如像 vscode

Kapture 2022-05-22 at 12.43.43

抽象開來,其實就是這樣一個佈局

Kapture 2022-05-22 at 12.48.15.gif

你也可以先看實際效果:CSS 可拉伸分欄佈局 2(codepen.io)

是不是非常靈活呢?其實純 CSS 也是可以實現這樣的效果的,一起看看吧

一、基本實現原理

實現這個效果需要藉助 resize 特性,可以天然的實現元素拉伸特性。最常見的就是 textarea,預設就是可以拉伸的

<textarea></textarea>

Kapture 2022-05-22 at 13.02.04

不過,我們這裡需要的不是textarea,而是普通的元素。普通的元素要實現這樣的效果也很容易,只需要在overflow不是visible的情況下,新增resize屬性就行了

.resizable {
  resize: both;
  overflow: scroll;
}

Kapture 2022-05-22 at 13.11.41

原理就是這麼簡單,那麼如何運用呢?

二、自定義右下角 resize

雖然看似可以拉伸,但是可拉伸範圍實在是太小了。該如何增加可拉伸範圍呢?這裡有兩種思路:

  1. 通過偽元素自定義
  2. 容器整體放大

先說第一種思路。要定義尺寸,首先需要搞明白 resize指的是什麼?經過簡單的測試發現,在 chrome 中,resize其實就是橫縱滾動條的交界處,比如直接設定滾動條的尺寸

::-webkit-scrollbar {
  width: 20px; 
  height: 20px;
  background-color: rosybrown;
}

image-20220522133542156

現在將滾動條高度改大一點

::-webkit-scrollbar {
  width: 20px; 
  height: 100px;
  background-color: rosybrown;
}

可以看到 resize也跟著變化了

image-20220522133832539

當滾動條高度足夠大時,右側就變成整條都可以拉伸了

::-webkit-scrollbar {
  width: 20px; 
  height: 100vh;
  background-color: rosybrown;
}

Kapture 2022-05-22 at 13.41.46

然後,這個斜線條紋可以用偽元素::-webkit-resizer來修改

div::-webkit-resizer{
  background-color: royalblue;
}

Kapture 2022-05-22 at 13.46.08

不過遺憾的是,這種方式只適合-webkit-瀏覽器,所以firefox就不行了。

下面來看第二種思路:整體放大。

這裡說的整體放大,指的是將整個容器通過transform進行放大,這樣一來,右下角的resize也會跟隨放大了,比如

div{
  width: 300px;
  height: 20px;
  transform: scaleY(100);/*足夠大的放大倍數*/
  overflow: scroll;
  resize: horizontal;
}

image-20220522140045252

這樣也能達到整條側邊都可以拉伸的目的了。

下面來看實際應用吧

三、兩欄拉伸佈局

循序漸進,先實現兩欄佈局。比如這樣
image-20220522140859457

通常這類佈局都有一定的約束,比如這裡的 MAIN是自適應空間的,SIDE是固定尺寸,先快速寫出佈局和樣式

<div class="con">
  <aside>
    SIDE
  </aside>
  <main>
    MAIN
  </main>
</div>

關鍵樣式如下

.con{
  display: flex;
}
aside{
  width: 200px;
}
main{
  flex: 1;
}

那麼,如何讓左邊側邊欄居右拖拽功能呢?很簡單,將剛才可拉伸的元素放入側邊欄,讓寬度由裡面的拉伸元素決定(flex 子元素天然支援該特性),內容著用絕對定位覆蓋來實現,實現如下

<aside>
  <div class="resize"></div>
  <div class="line"></div>
  <section>SIDE</section>
</aside>

由於 firefoxresize無法自定義,所以這裡單獨一個標籤來模擬拉伸軸

aside{
  position: relative;
  overflow: hidden;

}
.resize{
  width: 200px;
  height: 16px;
  transform: scaleY(100);
  overflow: scroll;
  resize: horizontal;
  opacity: 0;
}
.line{
  position: absolute;
  top: 0;
  right: 0;
  width: 4px;
  bottom: 0;
  background-color: royalblue;
  opacity: 0;
  transition: .3s;
  pointer-events: none;
}

.resize:hover+.line,
.resize:active+.line{
  opacity: 1;
}

這樣就實現了左邊側邊欄拉伸的功能

Kapture 2022-05-22 at 14.27.51.gif

四、三欄拉伸佈局

有時候會出現兩邊側邊欄,中間部分是自適應的,如下

image-20220522143137031

這種如何實現呢?首先按照兩欄佈局的思路

<div class="con">
  <aside>
    <div class="resize"></div>
    <div class="line"></div>
    <section>SIDE</section>
  </aside>
  <main>
    MAIN
  </main>
  <aside class="right">
    <div class="resize"></div>
    <div class="line"></div>
    <section>SIDE</section>
  </aside>
</div>

不過這樣會有一個很明顯的問題,由於resize是在右側,如果放在右邊側邊欄,那肯定就相反了,具體表現就是,往右拉伸,右側邊欄反而增大,不符合直覺

有沒有什麼辦法讓resize到左邊來呢?

首先想到的是通過翻轉變換,水平方向上翻轉可以scaleX(-1)來實現,合併起來就是

.right .resize{
  transform: scale(-1, 100);
}

在 Chrome 下表現不錯

Kapture 2022-05-22 at 14.50.24.gif

但是,Firefox 就有點奇怪了

Kapture 2022-05-22 at 14.52.55.gif

朝右邊拖拽,右側邊欄寬度反而增加。究其原因,在 Firefox上,transform水平翻轉僅僅改變了視覺上的效果,並沒有改變互動行為,有沒有辦法可以真正改變resize的位置呢?

還真有,不過僅可以改變水平方向上的位置,通過direction屬性。這是一個可以改變文件方向流的屬性,有些語言方向是從右往左的,所以設定後,resize 也從右側變到了左側。

image-20220522150237839

所以實現就是

.right .resize{
  direction: rtl;
}

這樣下來,Chrome 和 Firefox 均正常了

Kapture 2022-05-22 at 15.06.27.gif

完整程式碼可以訪問:CSS 可拉伸分欄佈局 (codepen.io)或者CSS 可拉伸分欄佈局 (juejin.cn)

五、其他組合分欄效果

組合分欄效果少不了垂直方向上的。

垂直方向的分欄和水平方向大同小異,只需要水平方向上縮放足夠大就行了。

.resize-top{
  width: 16px;
  resize: vertical;
  transform: scaleX(100);
}

現在可以嘗試實現文章開頭佈局效果

<div class="con">
  <aside>
    <div class="resize"></div>
    <div class="line"></div>
    <section>SIDE</section>
  </aside>
  <main>
    <div class="main">
      MAIN
    </div>
    <footer>
      <div class="resize resize-top"></div>
      <div class="line"></div>
      <section>bottom</section>
    </footer>
  </main>
</div>

不過有個缺陷就是,無法通過direction或者其他手段,將resize真正地從下移到上面

image-20220522152911530

只能通過transform: scale(-1, 100);實現了,這樣就導致垂直方向上的拉伸在 Firefox 體驗不佳。效果如下

Chrome 表現完美:

Kapture 2022-05-22 at 12.48.15.gif

Firefox 表現差強人意:

Kapture 2022-05-22 at 16.08.08.gif

完整程式碼可以訪問:CSS 可拉伸分欄佈局 2(codepen.io) 或者CSS 可拉伸分欄佈局 2(juejin.cn)

繼續調整一下,還可以實現這樣的佈局效果

Kapture 2022-05-22 at 16.11.14.gif

完整程式碼可以訪問:CSS 可拉伸分欄佈局 3(codepen.io) 或者CSS 可拉伸分欄佈局 3(juejin.cn)

六、總結一下

以上就通過純 CSS 實現了4種常見的分欄拉伸效果,基本能滿足常見的佈局需求了,是不是非常實用呢?下面總結一些要點

  1. 實現原理是 CSS resize 屬性
  2. resize 生效的條件是 overflow 不能是 visible
  3. resize 在 Chrome 下其實是橫縱滾動條的交匯處,改變滾動條尺寸可以改變 resize 大小
  4. resize 還可以通過縮放整體容器來實現
  5. resize 預設是在右下角,可以通過水平翻轉到左下角,Chrome 完美支援拉伸,Firefox 不行
  6. 還可以通過 direction 改變文件流的方式,將 resize 從右下角變到左下角
  7. 垂直方向上 resize 只能通過 transform 翻轉方式實現,因此 Firefox 體驗較差

雖然 Firefox 在垂直方向上略有缺陷,如果你對相容性沒太多需求,比如公司內部專案,Electron 應用等,那就放心大膽的使用吧,千萬不要被相容性束縛了你的思維。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探

相關文章