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开发chrome插件,实现获取界面数据和保存到数据库功能
Dec 01 Vue.js
在vue中使用inheritAttrs实现组件的扩展性介绍
Dec 07 Vue.js
vue项目中企业微信使用js-sdk时config和agentConfig配置方式详解
Dec 15 Vue.js
vue 通过base64实现图片下载功能
Dec 19 Vue.js
Vue实现简易购物车页面
Dec 30 Vue.js
Vue过滤器,生命周期函数和vue-resource简单介绍
Jan 12 Vue.js
vue实现按钮切换图片
Jan 20 Vue.js
vue仿携程轮播图效果(滑动轮播,下方高度自适应)
Feb 11 Vue.js
Vue如何实现变量表达式选择器
Feb 18 Vue.js
vue项目多环境配置(.env)的实现
Jul 21 Vue.js
一篇文章告诉你如何实现Vue前端分页和后端分页
Feb 18 Vue.js
解决vue-router的beforeRouteUpdate不能触发
Apr 14 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的类 功能齐全的发送邮件类
2006/10/09 PHP
php中的一些数组排序方法分享
2012/07/20 PHP
PHP类中的魔术方法(Magic Method)简明总结
2014/07/08 PHP
PHP中使用imagick生成PSD文件缩略图教程
2015/01/26 PHP
解析WordPress中的post_class与get_post_class函数
2016/01/04 PHP
在Win2003(64位)中配置IIS6+PHP5.2.17+MySQL5.5的运行环境
2016/04/04 PHP
深入理解PHP原理之执行周期分析
2016/06/01 PHP
boxy基于jquery的弹出层对话框插件扩展应用 弹出层选择器
2010/11/21 Javascript
jQuery EasyUI API 中文文档 - ComboTree组合树
2011/10/11 Javascript
JS随机漂浮广告代码具体实例
2013/11/19 Javascript
JS关键字球状旋转效果的实例代码
2013/11/29 Javascript
删除javascript中注释语句的正则表达式
2014/06/11 Javascript
js移动焦点到最后位置的简单方法
2016/11/25 Javascript
AJAX和jQuery动态加载数据的实现方法
2016/12/05 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
2020/10/26 Javascript
JavaScript方法_动力节点Java学院整理
2017/06/28 Javascript
Vue.js结合Ueditor富文本编辑器的实例代码
2017/07/11 Javascript
基于vue v-for 循环复选框-默认勾选第一个的实现方法
2018/03/03 Javascript
微信小程序 Animation实现图片旋转动画示例
2018/08/22 Javascript
详解ES6系列之私有变量的实现
2018/11/21 Javascript
基于vue-cli、elementUI的Vue超简单入门小例子(推荐)
2019/04/17 Javascript
cordova+vue+webapp使用html5获取地理位置的方法
2019/07/06 Javascript
vue使用svg文件补充-svg放大缩小操作(使用d3.js)
2020/09/22 Javascript
ajax jquery实现页面某一个div的刷新效果
2021/03/04 jQuery
Python抓包程序mitmproxy安装和使用过程图解
2020/03/02 Python
keras实现调用自己训练的模型,并去掉全连接层
2020/06/09 Python
浅谈pytorch中torch.max和F.softmax函数的维度解释
2020/06/28 Python
CSS3 background-image颜色渐变的实现代码
2018/09/13 HTML / CSS
详解前端HTML5几种存储方式的总结
2016/12/27 HTML / CSS
大学毕业生工作的自我评价
2013/10/01 职场文书
大学在校生求职信范文
2013/11/21 职场文书
有关爱国演讲稿
2014/05/07 职场文书
市场策划求职信
2014/08/07 职场文书
民间借贷纠纷案件代理词
2015/05/26 职场文书
Opencv中cv2.floodFill算法的使用
2021/06/18 Python