Vue3 实现双盒子定位Overlay的示例


Posted in Vue.js onDecember 22, 2020

在 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', calculate, true);
 window.removeEventListener('resize', calculate);
});

至此,已经完成基本的双盒子定位法的 Overlay 的设计。

小结

通过双盒子定位来构建的 Overlay 能够有效规避 CSS 带来的问题 zindex 等一系列相关的问题,只用通过计算盒子之间的相对偏移,就能让 Panel 附着于 Origin 上,这样,实现类似下拉或者 Tooltip 等功能的时候,就会非常有用。同时,附上一个简单例子,希望能带来一些启发。

以上就是Vue3 实现双盒子定位Overlay的示例的详细内容,更多关于vue3 实现Overlay的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
Vue组件生命周期运行原理解析
Nov 25 Vue.js
vue实现树状表格效果
Dec 29 Vue.js
vue3弹出层V3Popup实例详解
Jan 04 Vue.js
vue实现一个获取按键展示快捷键效果的Input组件
Jan 13 Vue.js
vue-resource 拦截器interceptors使用详解
Jan 18 Vue.js
vue使用transition组件动画效果的实例代码
Jan 28 Vue.js
vue使用v-model进行跨组件绑定的基本实现方法
Apr 28 Vue.js
vue-cil之axios的二次封装与proxy反向代理使用说明
Apr 07 Vue.js
VUE之图片Base64编码使用ElementUI组件上传
Apr 09 Vue.js
vue特效之翻牌动画
Apr 20 Vue.js
vue 自定义组件添加原生事件
Apr 21 Vue.js
解决vue自定义组件@click点击失效问题
Apr 30 Vue.js
详解Vue的异步更新实现原理
Dec 22 #Vue.js
Vue组件简易模拟实现购物车
Dec 21 #Vue.js
vue实现购物车的小练习
Dec 21 #Vue.js
Vue实现小购物车功能
Dec 21 #Vue.js
vue监听滚动事件的方法
Dec 21 #Vue.js
vue el-upload上传文件的示例代码
Dec 21 #Vue.js
vue 在单页面应用里使用二级套嵌路由
Dec 19 #Vue.js
You might like
杏林同学录(二)
2006/10/09 PHP
PHP测试程序运行时间的类
2012/02/05 PHP
CentOS 上搭建 PHP7 开发测试环境
2017/02/26 PHP
JS 控制CSS样式表
2009/08/20 Javascript
jQuery EasyUI API 中文文档 - MenuButton菜单按钮使用介绍
2011/10/06 Javascript
JavaScript单元测试ABC
2012/04/12 Javascript
Js控制弹窗实现在任意分辨率下居中显示
2013/08/01 Javascript
ExtJs中gridpanel分组后组名排序实例代码
2013/12/02 Javascript
js获取当前地址 JS获取当前URL的示例代码
2014/02/26 Javascript
JS绘制生成花瓣效果的方法
2015/08/05 Javascript
JQuery实现左右滚动菜单特效
2015/09/28 Javascript
jQuery多条件筛选如何实现
2015/11/04 Javascript
JS中改变this指向的方法(call和apply、bind)
2016/03/26 Javascript
jQuery qrcode生成二维码的方法
2016/04/03 Javascript
jQuery中的基本选择器用法学习教程
2016/04/14 Javascript
从零开始学习Node.js系列教程六:EventEmitter发送和接收事件的方法示例
2017/04/13 Javascript
countUp.js实现数字滚动效果
2019/10/18 Javascript
javascript canvas API内容整理
2020/02/16 Javascript
Angular短信模板校验代码
2020/09/23 Javascript
小程序点餐界面添加购物车左右摆动动画
2020/09/23 Javascript
haskell实现多线程服务器实例代码
2013/11/26 Python
在Django的视图(View)外使用Session的方法
2015/07/23 Python
Python3连接MySQL(pymysql)模拟转账实现代码
2016/05/24 Python
Apache如何部署django项目
2017/05/21 Python
详解django三种文件下载方式
2018/04/06 Python
使用python存储网页上的图片实例
2018/05/22 Python
Python 一句话生成字母表的方法
2019/01/02 Python
python实现视频分帧效果
2019/05/31 Python
全面解析CSS Media媒体查询使用操作(推荐)
2017/08/15 HTML / CSS
蔻驰意大利官网:COACH意大利
2019/01/16 全球购物
Footshop乌克兰:运动鞋的最大选择
2019/12/01 全球购物
本科生求职简历的自我评价
2013/10/21 职场文书
教育学习自我评价
2014/02/03 职场文书
自荐书范文范例
2014/02/13 职场文书
计划生育诚信协议书
2014/11/02 职场文书
2021好看的国漫排行榜前十名 《完美世界》上榜,《元龙》排名第一
2022/03/18 国漫