likes
comments
collection
share

深入剖析浏览器滚动条

作者站长头像
站长
· 阅读数 19

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

前言

为什么要专门写一篇关于浏览器滚动条的文章呢?毕竟定制滚动条属于小众需求。事情源于一次产品上线之后,用户反馈滚动条也太丑了,设计让前端帮忙改一下:要细一些,宽度6px,颜色淡一些,#000000 8%不透明度,hover状态时宽度 10px,颜色 #000000 16%不透明度

于是借此机会对滚动条做了一番研究,发现这里面的水还是很深的(网上的文章很多关于滚动条的文章都是纰漏百出,人云亦云),在此我把研究结果向大家分享一下,一个小小的滚动条中竟然包含了以下六种伪元素

  • ::-webkit-scrollbar——整个滚动区域
  • ::-webkit-scrollbar-button——滚动条两端的按钮(包含上下箭头)
  • ::-webkit-scrollbar-track——滚动条外层轨道
  • ::-webkit-scrollbar-track-piece——滚动条内层轨道(分为上下两部分)
  • ::-webkit-scrollbar-thumb——可拖动的滑块
  • ::-webkit-scrollbar-corner——垂直滚动条和水平滚动条交汇部分(通常在右下角)

可以用下面的一张图来描述上述所有元素:

深入剖析浏览器滚动条

看起来是不是很复杂?别担心,我会从下面这个基础的原生滚动条开始,带领大家一步步认识上面的各种伪元素:

深入剖析浏览器滚动条

上面矩形滚动区域的 HTML 结构为:

<div class="container">
  <div class="rect">
    <div class="box"></div>
  </div>
</div>

初始 CSS 样式为:

.container {
  margin-top: 50px;
  display: flex;
  justify-content: space-around;
}

.rect {
  width: 600px;
  height: 600px;
  overflow: scroll;
  border: 1px solid gainsboro;
}

.box {
  width: 1000px;
  height: 1000px;
  background: linear-gradient(45deg, #fb3 25%, #58a 0, #58a 50%, #fb3 0, #fb3 75%, #58a 0);
  background-size: 42.4264px 42.4264px;
  opacity: 0.2;
}

-webkit-scrollbar 伪元素

::-webkit-scrollbar代表整个滚动条,我们可以为其设置宽高、背景色、边框,例如:

.rect::-webkit-scrollbar {
  width: 50px;
  height: 50px;
  background-color: yellow;
  border: 5px solid green;
}

然后滚动条就变成了下面这个样子:

深入剖析浏览器滚动条

值得注意的是,对于自定义滚动条样式,这个伪类是必不可少的,如果没有设置 ::-webkit-scrollbar,那么下面所有其他的伪类都无法生效。所以该伪类的一个重要的作用就是告诉控制 webkit 引擎是否使用默认的滚动条样式。

另外需要特别强调的是,在 -webkit-scrollbar 伪类样式中,width 属性只对垂直滚动条起作用,height 属性只对水平滚动条起作用,而这个规则对下面的 -webkit-scrollbar-button则恰恰相反。

-webkit-scrollbar-button 伪元素

::-webkit-scrollbar-button伪元素表示滚动条两端的按钮区域。如果是垂直滚动条,按钮在上下两端;如果是水平滚动条,按钮在左右两端。该伪元素要配合以下六个伪类一起使用:

伪类选中的状态
:horizontal当滚动条处于水平方向
:vertical当滚动条处于垂直方向
:decrement当点击后减少滚动值
:increment当点击后增加滚动值
:start当处于开始位置
:end当处于结束位置

我们增加以下 CSS 代码来设置滚动按钮样式:

.rect::-webkit-scrollbar-button {
  display: block;
  background-color: rgba(185, 28, 28, 0.8);
  background-repeat: no-repeat;
  background-size: 50%;
  background-position: center;
  height: 25px;
  width: 25px;
}

.rect::-webkit-scrollbar-button:vertical:start:increment {
  background-image: url("https://img.zlib.cn/blog/chevron-down.svg");
}

.rect::-webkit-scrollbar-button:vertical:start:decrement {
  background-image: url("https://img.zlib.cn/blog/chevron-up.svg");
}

.rect::-webkit-scrollbar-button:vertical:end:increment {
  background-image: url("https://img.zlib.cn/blog/chevron-down.svg");
}

.rect::-webkit-scrollbar-button:vertical:end:decrement {
  background-image: url("https://img.zlib.cn/blog/chevron-up.svg");
}

.rect::-webkit-scrollbar-button:horizontal:start:increment {
  background-image: url("https://img.zlib.cn/blog/chevron-right.svg");
}

.rect::-webkit-scrollbar-button:horizontal:start:decrement {
  background-image: url("https://img.zlib.cn/blog/chevron-left.svg");
}

.rect::-webkit-scrollbar-button:horizontal:end:increment {
  background-image: url("https://img.zlib.cn/blog/chevron-right.svg");
}

.rect::-webkit-scrollbar-button:horizontal:end:decrement {
  background-image: url("https://img.zlib.cn/blog/chevron-left.svg");
}

效果如下:

深入剖析浏览器滚动条

刚才上面也讲到,在 -webkit-scrollbar-button伪元素里面,width 只对水平滚动条两端的按钮起作用,height 只对垂直滚动条两端的按钮起作用。

-webkit-scrollbar-track 伪元素

::-webkit-scrollbar-track表示滚动条的外层轨道,我们先给它增加以下 CSS 代码看看效果:

.rect::-webkit-scrollbar-track {
  background-color: blue;
  border: 5px solid orange;
  margin: 30px;
}

这里特地增加了 margin 外边距把 scrollbar 本身的背景色给透出来方便大家观察其位置:

深入剖析浏览器滚动条

上面的蓝色背景橙色边框区域就是外层轨道的,需要注意的是,这里无法单独对其设置 width 或 height 样式,它的宽高始终和 -webkit-scrollbar的宽高保持一致。既然是外层轨道,说明存在内层轨道,我们继续往下看:

-webkit-scrollbar-track-piece 伪元素

::-webkit-scrollbar-track-piece滚动条的内层轨道,这个就有点意思了,为了看起来更有辨识度,我们用颜色区分出来:

.rect::-webkit-scrollbar-track-piece {
  background-color: green;
  border: 5px solid purple;
  margin: 30px;
}

细心的你可能已经发现了,这次的样式非常奇怪,因为中间有一道杠:

深入剖析浏览器滚动条

这是因为内层轨道其实是有两部分组成的,垂直滚动条的内层轨道被分为上下部分,水平滚动条的内层轨道被分为左右两部分,而每一部分都可以用 startend伪类来控制,例如设置不同的背景色:

/* 控制滚动区域的上/左半部分 */
.rect::-webkit-scrollbar-track-piece:start {
  background-color: yellowgreen;
}

/* 控制滚动区域的下/右半部分 */
.rect::-webkit-scrollbar-track-piece:end {
  background-color: greenyellow;
}

通过设置背景色区分,可以看到更明显的看到上下/左右两部分的范围了:

深入剖析浏览器滚动条

-webkit-scrollbar-thumb 伪元素

::-webkit-scrollbar-thumb才是滚动条的滑块,也就是可拖动的部分,我们用下面的 CSS 把它展现出来:

.rect::-webkit-scrollbar-thumb {
  background-color: rgba(128, 0, 128, 0.5);
  border-radius: 30px;
}

可以看到滚动滑块的中间恰好是内层轨道上下/左右两部分的交界处:

深入剖析浏览器滚动条

这里有一点需要特别注意,很多人想通过 width 来修改垂直滑块的宽度,或者通过 height 来修改水平滑块的高度,其实都是不可行的,因为它的宽高永远和 -webkit-scrollbar保持一致,那怎样从视觉上让其变得更窄一点呢?这里要用到 background-clip这个 CSS 属性:

background-clip 用于设置元素的背景(包括背景颜色或背景图片)是否延伸到边框、内边距盒子和内容盒子下面。

它可以有以下取值:

  • background-clip: border-box:背景延伸至 border 外沿
  • background-clip: padding-box:背景延伸至 padding 外沿
  • background-clip: content-box:背景延伸至 content 外沿
  • background-clip: text:背景剪裁成文字的前景色

我们给滚动滑块设置边框透明和背景范围:

.rect::-webkit-scrollbar-thumb {
  background-color: rgba(128, 0, 128, 0.5);
  border-radius: 30px;

  border: 15px solid transparent; /* 设置边距透明 */
  background-clip: padding-box; /* 设置背景范围 */
}

可以看到从视觉上看滚动滑块的宽度/高度发生了改变:

深入剖析浏览器滚动条

-webkit-scrollbar-corner 伪元素

::-webkit-scrollbar-corner垂直滚动条和水平滚动条交界的部分,我们可以单独控制其样式,例如添加背景色:

.rect::-webkit-scrollbar-corner {
  background-color: #7f1d1d;
}

注意看到右下角区域的变化:

深入剖析浏览器滚动条

学习总结

讲完这六个伪元素,再回过来看下面的图,就非常清晰了:

深入剖析浏览器滚动条

可以看到滚动条在设计上是非常复杂的,但正因为此,才可以满足更多的个性化定制需求,虽然带有 -webkit-前缀,不是 w3c 标准,但是由于 Chrome 一统天下,这已经成为事实上的浏览器标准了。

实战

再回到最初的需求:要细一些,宽度6px,颜色淡一些,#000000 8%不透明度,hover状态时宽度 10px,颜色 #000000 16%不透明度。假设现在滚动区域是一个类为 rect 的 div,结构如下:

<div class="rect">
  <!-- 滚动区域 -->
</div>

最初写的 CSS 代码如下:

.rect::-webkit-scrollbar {
  width: 6px;
  height: 0;
  background-color: transparent;
}

.rect::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.08);
}

.rect::-webkit-scrollbar-thumb:hover {
  width: 10px;
  background-color: rgba(0, 0, 0, 0.16);
}

结果发现滚动条的颜色生效了,但是 hover 上去之后宽度并没有变宽,这是因为为 -webkit-scrollbar-thumb单独设置宽度是无效的,必须借用 background-clip来实现类似效果:

.rect::-webkit-scrollbar {
  width: 10px;
  height: 0;
  background-color: transparent;
}

.rect::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.08);
  border-left: 4px solid transparent;
  background-clip: padding-box;
}

.rect::-webkit-scrollbar-thumb:hover {
  background-color: rgba(0, 0, 0, 0.16);
  border: 0;
}

最终效果如下:

深入剖析浏览器滚动条