阅读 653

fixed布局踩坑引发的深思

故事背景

scene 1:前几天看到同事在处理一个页面 bug,大致是有一个输入组件需要点击后置顶,ta 设计成点击后应用 fixed 布局,设置了 top = 0,left = 0,但是看起来组件并没有贴到最顶层,查看页面元素,其中该组件的祖先元素设置了 transform 属性。

scene 2:当天晚上,一时兴起,我写了一个遮罩+弹窗提示的功能,遮罩和弹窗用到了fixed布局,其中遮罩实现了背景模糊的功能。本以为能让弹窗居中,却没有达到预期的效果,弹窗贴到了屏幕底部。

知识点

fixed 定位: 元素会被移出正常文档流,并不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transformperspective 或 filter 属性非 none 时,容器由视口改为该祖先。 ——源自《position - CSS(层叠样式表) | MDN (mozilla.org)》

作为 css 菜鸟的我,并不知道其实 fixed 定位不是一定就相对于屏幕视口定位的,而是在某几种 case 下,会相对于某些祖先元素进行定位,如上文 MDN 文档所说的。我一开始还在想会不会跟父容器也设置了 fixed 定位有关系,ε=(´ο`*)))唉。

好巧不巧,那个同事给祖先元素利用transform进行了一个平移操作,而我给弹窗的父组件,也就是遮罩,设置了一个背景模糊的效果——通过 backdrop-filter: blur(4px)实现(mdn可没提这个啊,filter包括了backdrop-filter ?),都刚好触及了fixed定位的特例,因此导致没有相对视口定位。

bug 复现与改进

核心代码大致如下

const Modal = (/*...*/) => {   // ...   const styleModal: CSSProperties = {     boxShadow: '0 2px 10px var(--shadow-color)',     minWidth: '200px',     maxWidth: '300px',     minHeight: '150px',     position: 'fixed',     top: '50%',     left: '50%',     zIndex: 101,     padding: '10px',     transform: 'translate(-50%, -50%)',     color: 'var(--text-color)',     background: 'var(--bg-color)',     wordBreak: 'break-all'   }   const styleMask: CSSProperties = {     width: '100vw',     height: '100vw',     top: 0,     left: 0,     position: 'fixed',     backdropFilter: 'blur(10px)',     zIndex: 100,     background: '#33333333'   }   // ...   return (     <div style={styleMask}>       <div className='' style={styleModal}>        <!-- ... -->       </div>     </div>   ) } export default Modal 复制代码

原本我以为是这样的

image.png 结果是这样的

image.png

原因在上一节已经说了,那么怎样实现我想要的,弹窗居中的效果呢?

方案一:将里面的弹窗元素的 position 改为 sticky sticky会相对于最近可滚动祖先定位,只要没有特别设置祖父容器的overflow,就是相对根元素定位,达到和fixed定位相同的效果。

方案二:将fixed布局元素放在最顶层标签 只要没有存在特殊的父元素,就不会受父容器的影响,简单直接,对于多个fixed布局的元素还可以设置z-index控制彼此的层级关系。

后记

11月1日回到家已经是10点多了,本想在12点前发出在掘金的第一篇文章的,结果临近12点都没写到干货,想匆匆提交,却连标题都没找到在哪输入,时间便已走到了0:00,既然都已经到第二天了,那不如好好写,写出点实用的东西来。

延伸

怎么让遮罩+弹窗的效果不那么单调?

原始效果:后面有一个遮罩,半透明灰色(background 设置 rgba),背景模糊(backdrop-filter:blur(10px),前面是一个弹窗,加一些边框阴影(box-shadow),居中显示(fixed定位通过 left:50%;top:50% 使弹窗左上角居中,其中百分比相对于父元素,设置 transform: translate(-50%, -50%) 进行补偿性位移,其中百分比相对于自身),用户调用封装好的函数 showToast 立即显示遮罩和弹窗。遮罩的主要作用是防止弹窗以外区域被点击。

进阶效果:showToast的时候增加一个动画过渡——弹窗上浮。 在这里将遮罩和弹窗的样式抽象成两个class: .mask.modal。 样式代码如下所示。其中的一个细节是定义动画帧的时候用上了calc()属性,功能顾名思义就是计算一个表达式,它可以混用百分比和像素单位,从而能够让我通过transform: translate(-50%, calc(-50% + 30px));精准地控制动画起点弹窗处在居中靠下偏移 30px 的位置。

.mask {   width: 100vw;   height: 100vh;   top: 0;   left: 0;   position: fixed;   backdrop-filter: blur(10px);   z-index: 100;   background: #33333333; } .modal {   box-shadow: 0 2px 10px var(--shadow-color);   min-width: 200px;   max-width: 300px;   min-height: 150px;   position: sticky;   top: 50%;   left: 50%;   z-index: 101;   padding: 10px;   color: var(--text-color);   background: var(--bg-color);   word-break: break-all;   animation: float ease-out 600ms forwards; } @keyframes float {   from {     transform: translate(-50%, calc(-50% + 30px));     opacity: 0;   }   to {     transform: translate(-50%, -50%);     opacity: 1;   } }


作者:月藤
链接:https://juejin.cn/post/7025631867801960455


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐