自定义Scrollbar样式
旧题新解,自定义scrollbar样式,是一个前端程序员经常遇到的问题了,答案也是一搜一大堆,滚动条的样式属性:
::-webkit-scrollbar
:控制滚动条整体::-webkit-scrollbar-track
:控制滚动条的轨道::-webkit-scrollbar-thumb
:控制滚动条的拖拽部分
以上属性,基本就够我们定义一个好看的滚动条样式了,还有一些不常用的属性:
::-webkit-scrollbar-button
:控制滚动条上下(或左右)两端的按钮::-webkit-scrollbar-track-piece
:控制滚动条轨道中未被可拖拽部分覆盖的区域::-webkit-scrollbar-corner
:控制垂直滚动条和水平滚动条交汇的区域
不同的浏览器,样式属性上会有差别,写的时候要注意。
不过这都是老生常谈的话题了,我们现在做一个项目不可能说每个出现滚动条的地方,我们都去把自定义好的样式重新再写一遍。
于是,就出现了样式的封装和复用。
在现在前端框架流行的时代,CSS的预编译框架
也为我们提供了样式封装和复用的语法:mixin
当然了,如果只是写个mixin
,然后把样式写进去,好像也没啥新意,怎么玩呢?
接下来我就给大家说一下我在写项目时,简单封装过的一个使用scss
封装的scrollbar
的mixin
。
也不算多高级,有想法的朋友也可以在此基础上继续扩展,小生献丑了。
进入正文
友情提示1:得有一定的
scss
语法基础哦,less
也行,基本也能看懂,如果二者都没有,那么有些用法可能会看不懂。
友情提示2:完整的文件代码我会放到文章最后,有需要的自取。
入口函数
首先,定义一个scrollbar
的mixin
这是基础:
@mixin scrollbar {}
其次就要想了,样式的封装和复用本质和封装一个函数没啥区别嘛,所以,为了人人可用,我们还需要让使用者提供自己的名字即传类名进来:
@mixin scrollbar($class) {}
参数处理
再想一下,传了类名就完了吗?
-
我经常碰到一个问题,那就是我自己在使用我写的
scrollbar
这个mixin
时,我有时会纠结,我要传的$class
参数是个啥样的?是传一个.abc
呢,还是直接传abc
呢? -
我经常会忘,所以每次都得翻看之前怎么写的,或者看看
mixin
中的定义,很烦。
由此,为了应对这种情况,我们还需要有能自动帮我们处理$class
这个传入的类名参数的逻辑,那就在写一个函数吧,毕竟封装就要做到逻辑清晰-功能单一。
@use "sass:string";
@function classHandle($class) {}
封装这个函数要用到scss
的内置模块string
,通过@use
引入即可。
PS:
string
内置模块包含了很多函数包,比如:
quote
函数,可以将传入的参数作为带引号的字符串返回。slice
可以进行字符串剪切.unquote
可以将字符串进行反解析,变成不带引号的字符串返回。想深入了解的,可以去scss官网学习下,目前了解这么多就够用了。
这个函数长这样:
@use "sass:string";
@function classHandle($class) {
$c: string.quote($class);
@if string.slice($c, 1, 1)==string.quote(".") {
@return string.unquote($c);
}
@else {
@return string.unquote(".#{$c}");
}
}
这简单的逻辑,应该不用我多讲了吧:
- 将参数转换为字符串,定义一个变量接收一下,然后对字符串第一位剪切,判断是不是
.
,然后就是是否拼接.
,然后返回处理结果。
到此为止,类名参数的处理算是完成了。
样式添加
接下来要做的就是把样式添加进去了。
那添加之前我们要考虑一个问题,你说有没有滚动条是不需要我们定义颜色的?
我在使用ant-design
的table
组件的时候就碰到了这个问题,我只需要改变这个滚动条的宽高就好了,不需要设置样式,那如果我们把这些样式添加的逻辑写到一起,是不是会比较难搞,所以怎么办?
拆分呗,正好把样式的逻辑也拆分出去一部分,方便以后如果有其他的特殊情况我们也好处理。
@mixin addColor(){}
定义一个添加颜色的mixin
那你可能要问了,为啥不用@function
了?@function
不好用吗?
当然不是,是因为二者的使用环境不同:
@function
用来定义复杂操作的函数,接收参数并返回结果。@function
函数有一条规则就是必须用指定的return
语句@return
,用于返回函数调用的结果
@mixin
是用于定义样式的函数,这些样式可以在整个样式表中重复使用,从而允许编写可重复使用的样式规则。
了解了这些,接下来,我们就是常规操作了,写样式呗:
$-color: #c1c1c1;
@mixin addColor() {
/* 外层轨道 */
&::-webkit-scrollbar-track {
border-radius: 3px;
background: rgba($color: $-color, $alpha: 0.1);
}
/* 滚动的滑块 */
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background: rgba($color: $-color, $alpha: 0.2);
&:hover {
background: rgba($color: $-color, $alpha: 0.4);
}
}
}
需要注意的,我们定义的$-color
,再变量名面前,加上-
,意味着变量私有,外部不可使用。
另外有个小tips:
不同的样式,我们不一定非要写不同的颜色值,颜色的变化明暗变化也可以营造不同的效果,而且还一脉相承。
需要注意的是
rgba($color: $color, $alpha: 0.4)
这只是rgba
的一种写法,跟你直接写rgba(255,255,255,0)
一个道理;
逻辑整合
好了,说了这么多,我们基本完工了,现在需要的就是把这些逻辑整合起来,并放到入口函数中即可了。
@mixin scrollbar($class, $addColor:true){}
因为要知道使用者需不需要我们添加的scrollbar
颜色,所以需要一个额外的参数让使用者告诉我们是否要用,默认值为true
(要用)
现在,往里面添加逻辑:
@mixin scrollbar($class, $addColor:true){
$classname: classHandle($class);
::ng-deep #{$classname} {
/* 宽高 */
&::-webkit-scrollbar {
width: 0.6vw;
height: 0.8vh;
}
@if $addColor {
@include addColor();
}
}
}
这里面的逻辑也很简单:
-
首先处理传进来的类名,并定义一个
$classname
变量接收一下。 -
::ng-deep
这个是angular
中样式穿透的用法,如果你使用的vue
或者react
,只需要改成对应的即可. -
下面就是
scss
中样式的写法了,值得注意的是:width
代表的是垂直滚动条的宽,height
代表的是水平滚动条的高,莫得搞混了。不要以为我们定义的是一个宽度0.6,高度0.8的滚动条。 -
最后就是判断是不是要添加颜色了。
到此为止,一个简单的scrollbar
样式的mixin
就写好了,我会把完整的文件代码放到最后,有需要的自取。
私有成员
做到上面那一步,整个样式文件就已经完成了,但是还不是很严谨,因为我们都知道,一个class
类中的成员方法,有些是不对外暴露的,只允许类内部使用,这叫私有成员。
那变量的私有上面说了,方法怎么不对外暴露呢?
这就需要用到scss
的@forward
规则了:
- 通过在语句中添加
hide
或者show
关键字,后面加上成员名称来指定样式表的成员的可见性。 - 类似这样:
@forward './myStyle.scss hide privateFunc'
这就代表着隐藏myStyle
样式表中的privateFunc
成员 - 其他用法,还是参见官网啦:scss官网
那接下来就是文件划分啦,一般来说,我们定义的mixin
模块是单独的,我们还需要一个文件做整理和收集,不然在后续项目开发中,除了scrollbar
模块之外,我们有定义了好多其他模块,那我们还得在使用到的地方一个个引入吗?
所以,在定义一个mixin.scss
文件:
@forward "./mixins/scrollbar" hide classHandle, addColor;
这样我们只管在这个文件中引入其他模块,然后用到的地方直接简单的引入mixin.scss
就能使用所有想用的模块啦。
结尾
话说回来,你可能经常会简单有一些scss
文件名称是以_
开头的,比如_scrollbar.scss
,那这和正常的scrollbar.scss
有啥差别呢?有兴趣的可以去官网看下。
到此,本文就结束,这里给大家提供一下文章的源代码,以及文件结构。
scrollbar.scss
@use "sass:string";
/**
* class 类名处理
*/
@function classHandle($class) {
$c: string.quote($class);
@if string.slice($c, 1, 1)==string.quote(".") {
@return string.unquote($c);
}
@else {
@return string.unquote(".#{$c}");
}
}
/**
* 是否给滑块增加颜色
* [使用场景:适合一些特殊的不需要颜色的滚动条,如ant-table组件 .ant-table-header]
*/
$-color: #c1c1c1;
@mixin addColor() {
/* 外层轨道 */
&::-webkit-scrollbar-track {
border-radius: 3px;
background: rgba($color: $-color, $alpha: 0.1);
}
/* 滚动的滑块 */
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background: rgba($color: $-color, $alpha: 0.2);
&:hover {
background: rgba($color: $-color, $alpha: 0.4);
}
}
}
/**
* $class 类名
*/
@mixin scrollbar($class, $addColor: true) {
/* 处理 class 类名 */
$classname: classHandle($class);
::ng-deep #{$classname} {
/* 宽高 */
&::-webkit-scrollbar {
width: 0.6vw;
height: 0.8vh;
}
@if $addColor {
@include addColor();
}
}
}
mixins.scss
@forward "./mixins/scrollbar" hide classHandle, addColor;