惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
博客园 - 司徒正美
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog

戴兜的小屋

Hi! 2024 - 戴兜的小屋 [简中] Google Summer of Code & Chrome Extensions - 戴兜的小屋 Google Summer of Code & Chrome Extensions - 戴兜的小屋 SCSS+WindiCSS实现主题色切换 - 戴兜的小屋 Plaid CTF Writeup [Treasure Map/CSS] - 戴兜的小屋 写一个炫酷的个人名片页✨✨ - 戴兜的小屋 【戴兜的游戏安利】Hi-Fi RUSH,不可多得的动作爽游 - 戴兜的小屋 Manifest V3扩展Content Script绕过CSP限制点击页面内元素 - 戴兜的小屋 在 Nuxt.js 中配合 windicss 实现暗黑模式适配 - 戴兜的小屋 实现滚动时Header自动隐藏 - 戴兜的小屋 你好!2023 - 戴兜的小屋 !important导致TransitionGroup失效 - 戴兜的小屋 新年快乐! - 戴兜的小屋 CSS Layout API初探:瀑布流布局实现 - 戴兜的小屋 你好! 2022 - 戴兜的小屋 手撸一个前端天气卡片 - 戴兜的小屋 Hi!2021 - 戴兜的小屋 服务器又搬迁啦! - 戴兜的小屋 base64数据隐写实现原理分析 - 戴兜的小屋
优雅地实现滚动容器遮罩 - 戴兜的小屋
戴兜文章作者 · 2023-12-09 · via 戴兜的小屋

在设计前端页面时,常常会遇到这种情况:可滚动容器的边界并非父容器的边界,导致子元素溢出造成裁切,让页面产生比较怪异的视觉效果(左图)

添加遮罩之后,效果自然了许多(右图)

纯色遮罩

以上图的这种情况举例,我们需要做的,是在可滚动容器的顶部和底部分别放置一个线性渐变的纯色遮罩,遮挡生硬的裁切线。创建两个元素 .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 遮罩

上面的这种方法有许多缺陷:

  1. 引入了许多额外的元素,致使整体布局变得复杂。
  2. 蒙版覆盖在可滚动容器之上,需要使用 pointer-events: none; 避免影响滚动操作。
  3. 仅适用于父容器为纯色的场景,在父容器有透明度、有背景图案或渐变时,遮罩会露馅。

是否有一种方法,在不引入额外元素、不使用绝对定位的条件下,解决这些缺陷呢?这时候就可以用到 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 查看