在设计前端页面时,常常会遇到这种情况:可滚动容器的边界并非父容器的边界,导致子元素溢出造成裁切,让页面产生比较怪异的视觉效果(左图)
添加遮罩之后,效果自然了许多(右图)
纯色遮罩
以上图的这种情况举例,我们需要做的,是在可滚动容器的顶部和底部分别放置一个线性渐变的纯色遮罩,遮挡生硬的裁切线。创建两个元素 .top-mask
、.bottom-mask
来作为遮罩,遮罩的颜色与父容器背景一致,使用 absolute 定位。
.top-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 24px;
background: linear-gradient(to top, transparent 0%, white 100%);
}
.bottom-mask {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 24px;
background: linear-gradient(to bottom, transparent 0%, white 100%);
}
直接为可滚动容器设置 position: relative
是行不通的,这会导致遮罩跟随容器滚动。所以需要在可滚动容器外部再嵌套一层 relative 定位的元素,使两个遮罩根据其位置定位,最终的结构大概是下面这样的:
<!-- 无遮罩 -->
<div class="out-container">
<div class="scrollable-container">
<!-- 很多很多的子元素 -->
</div>
</div>
<!-- 有遮罩 -->
<div class="out-container">
<div class="relative-container">
<div class="top-mask"></div>
<div class="bottom-mask"></div>
<div class="scrollable-container">
<!-- 很多很多的子元素 -->
</div>
</div>
</div>
在 Codepen 查看演示
后续为了优化视觉效果,可以根据条件显示/隐藏对应的 mask 元素(滚动条在顶部时不显示 top-mask,反之亦然)
改进:Alpha 遮罩
上面的这种方法有许多缺陷:
- 引入了许多额外的元素,致使整体布局变得复杂。
- 蒙版覆盖在可滚动容器之上,需要使用
pointer-events: none;
避免影响滚动操作。 - 仅适用于父容器为纯色的场景,在父容器有透明度、有背景图案或渐变时,遮罩会露馅。
是否有一种方法,在不引入额外元素、不使用绝对定位的条件下,解决这些缺陷呢?这时候就可以用到 mask
CSS属性。mask
属性允许提供一张图片作为蒙版,改变元素的可视区域。我们只需要生成一个线性渐变,将其作为可滚动容器的蒙版即可。
使用linear-gradient创建一个多段的线性渐变,得到图中的蒙版效果。
linear-gradient(to bottom, transparent 0%, white 25px, white calc(100% - 25px), transparent 100%)
接着,将得到的渐变图案作为 mask 应用到滚动容器上,为了便于自定义,将这里的遮罩高度 25px
提取出来,以 CSS 变量的形式提供。下面是完整的样式:
.scrollable-container {
--show-top-mask: 0;
--show-bottom-mask: 0;
--mask-size: 25px;
--gradient: linear-gradient(to bottom, transparent 0%, white calc(var(--show-top-mask) * var(--mask-size)),white calc(100% - calc(var(--mask-size)*var(--show-bottom-mask))), transparent 100%);
-webkit-mask: var(--gradient);
mask: var(--gradient);
}
.top-mask {
--show-top-mask: 1;
}
.bottom-mask {
--show-bottom-mask: 1;
}
因为我们将容器两侧的遮罩合并到了一个线性渐变中,想要控制其中一侧的遮罩就不太容易了,为了实现遮罩的独立控制,额外定义了 --show-top-mask
和 --show-bottom-mask
变量。最终的效果如下图所示:
在 CodePen 查看
发表回复