1. 程式人生 > 程式設計 >Vue3 實現雙盒子定位Overlay的示例

Vue3 實現雙盒子定位Overlay的示例

在 Vue 3 中,使用 <Teleport> 可以很優雅的把某個元件渲染到根節點之外的節點,同時使其渲染的內容不喪失響應式和對應的生命週期函式呼叫。那麼基於此,用 <Teleport> 實現相對於某一元素的 Overlay 。 實際上,這篇文章跟 Vue3 的關係不大,只是通過 Vue3 講解一類 Overlay 的設計方法。

原理

要實現相對於某一元素的 Overlay 需要依靠兩個元素,Origin 和 Panel,Origin 表示相對於的元素,而 Panel 表示 Overlay 本身,實現方法主要有兩種。

文字流定位法,基於 position 的 absolute 和 relative 特性,將 Panel 形成相對於 Origin 的位置來定位的方式。

Overlay 基於 Origin 做固定偏移的雙盒子定位法,也就是本文需要講解的方法。
實現 首先,通過<Teleport>,能夠建立頂層 Overlay,也就是在根節點建立一個新的節點。

setup(_,ctx) {
 const originRef = ref<HTMLElement>();
 const panelRef = ref<HTMLElement>();
 const panelStyle = ref<CSSProperties>({ position: 'absolute' });
 // ...
 return () => (
  <>
   <div ref={originRef}>origin</div>

   <Teleport to="#cdk-overlay-anchor">
     <div style={{ position: 'fixed',top: 0,left: 0,right: 0,bottom: 0,height: '100vh',width: '100vw' pointerEvents: 'none'}}>
      <div ref={panelRef} style={panelStyle.value}>
       <div style={{height: '100px',width: '100px' border: '1px solid black'}} />
      </div>
     </div>
   </Teleport>
  </>
 );
}

拿到這兩者的 dom ref 後,需要通過實時計算 Origin 的盒子的大小和位置,來獲得 Panel 的相對偏移。在 Vue 中,元素只有在 mounted 後才能獲取得到,所以可以通過 composition-api 的 onMouted 來獲取具體元素。然後再在 生命週期中 進行計算。

計算兩個盒子的相對位置

如何計算 Origin 的大小和位置,以及獲取其變化後的監聽。Origin 的大小和位置,通過 getBoundingClientRect 這一 API 來獲取,這一就可以開始計算 Overlay 的相對位置。假設我們要把 Overlay 放在 Origin 的正下方,計算函式應該是這樣的。

const panelStyle = ref<CSSProperties>({ position: 'absolute' });

onMounted(() => {
 const origin = originRef.value;
 const panel = panelRef.value;
 if (!origin || !panel) {
  return ;
 }

 const calculate = () => {
  const rect = origin.getBoundingClientRect();
  // 原點為 origin 元素的底邊中央正下方
  const originX = rect.left + (rect.width / 2);
  const originY = rect.bottom;

  // panel的座標為到原點的偏移
  const panelRect = panel.getBoundingClientRect();
  const panelX = originX - panelRect.width / 2;
  const panelY = originY;

  // 設定 panel 資料,觸發節點變更
  panelStyle.value.left = `${panelX}px`;
  panelStyle.value.top = `${panelY}px`;
 };
});

當然,你還可以計算各個不同方向的 Panel 座標(比如,正左、正上、正下等),排列組合一下,一共有種27不同的情況(每個點依賴於兩個變數 X 和 Y;每個變數有三種不同的情況,左、中、右,或者,上、中、下)。

監聽盒子的變化

在這裡,我們將使用瀏覽器自帶的API 來對他們進行監聽。通過 MutationObserver 和 ResizeObserver,可以很輕鬆的監聽 Origin 和 Panel 的大小和位置變化。

首先是監聽 Origin 的大小和位置變化,這裡採用的是 MutationObserver,因為導致位置變化的原因只能是 style,所以只需要監聽 style 的變化即可。

const origin$ = new MutationObserver(calculate);
origin$.observe(origin,{
 // 只需要拿到 attribute 的 style 的變化即可
 attributeFilter: ['style'],});

Panel 只需要監聽其大小的變化,大小變化有一個更加完美的API, ResizeObserver。

const panel$ = new ResizeObserver(calculate);
panel$.observe(panel);

然後,需要在dom銷燬前取消監聽。

// dom銷燬前取消監聽
onBeforeUnmount(() => {
 origin$.disconnect();
 panel$.disconnect();
});

監聽視窗事件

為了能夠正確的獲取變化,我們需要監聽兩個事件:resize 和 scroll.

// 為了能夠在滾動事件捕獲前進行計算,帶有滾動條的子元素也會因此觸發計算
window.addEventListener('scroll',calculate,true);
window.addEventListener('resize',calculate);
最後,仍然要在銷燬前取消事件。

// dom銷燬前取消監聽
onBeforeUnmount(() => {
 window.removeEventListener('scroll',true);
 window.removeEventListener('resize',calculate);
});

至此,已經完成基本的雙盒子定位法的 Overlay 的設計。

小結

通過雙盒子定位來構建的 Overlay 能夠有效規避 CSS 帶來的問題 zindex 等一系列相關的問題,只用通過計算盒子之間的相對偏移,就能讓 Panel 附著於 Origin 上,這樣,實現類似下拉或者 Tooltip 等功能的時候,就會非常有用。同時,附上一個簡單例子,希望能帶來一些啟發。

以上就是Vue3 實現雙盒子定位Overlay的示例的詳細內容,更多關於vue3 實現Overlay的資料請關注我們其它相關文章!