index.html 文件 :

<div class='good'>请给我点赞</div>复制代码

index.scss 文件 :

.good { padding: 3px 6px; color: #333; background: rgba(#333, 0.1); border: 1px solid #333; border-radius: 3px; cursor: pointer; &:hover { color: #0069ff; background: rgba(#0069ff, 0.1); border: 1px solid #0069ff; } &.good-click { color: #0069ff; background: rgba(#0069ff, 0.1); border: 1px solid #0069ff; }}复制代码

index.js 文件 :

htmlcss案例总结7个工作中常用的css3案例带你懂得冷门却适用的特征 Vue.js

const goodBtn = document.querySelector('.good')goodBtn.addEventListener('click', () => { if (goodBtn.classList.contains('good-click')) { goodBtn.classList.remove(['good-click']) return } goodBtn.classList.add(['good-click'])})复制代码

是的,便是那么朴实无华,缺陷也暴露无遗:

相同的颜色我们利用了多次,比如 #333 和 #0069ff 。
如果有一天产品说把这个那个颜色改一下,细心点的你多动动手指也没啥问题,改就改了,但是这种办法很不“程序员”。

针对这个问题我们直策应用预处理器(SASS/LESS)的变量就完事了:

index.scss 文件 :

$color: #333;$hoverColor: #0069ff;.good { padding: 3px 6px; color: $color; background: rgba($color, 0.1); border: 1px solid $color; border-radius: 3px; cursor: pointer; &:hover { color: $hoverColor; background: rgba($hoverColor, 0.1); border: 1px solid $hoverColor; } &.good-click { color: $hoverColor; background: rgba($hoverColor, 0.1); border-color: $hoverColor; }}复制代码

咋一看已经是很好的实现办法了,但是也有缺点:

有时候(比如我大多数时候)都不想为了某一个分外的类下的 color 单独设置一个变量,仅仅只有它利用,我还要专门为其定义一个变量就显得代码很臃肿;在我添加了 good-click 这个类名后,我要把 color 、 border-color 、 background 全都重新设置一遍;

这个时候,css 原生变量 currentColor 即可大显技艺了。

改进方案

变量 currentColor 能拿到本元素的 color 属性的值,如果没有显示设置,拿的将会是父元素的 color 属性的值,由此类推。
借助这个特性,我们即可优化上述代码:

index.scss 文件 :

.good { position: relative; padding: 3px 6px; color: #333; border: 1px solid currentColor; border-radius: 3px; cursor: pointer; &::before { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: currentColor; opacity: 0.1; content: ''; } &:hover { color: #0069ff; } &.good-click { color: #0069ff; }}复制代码

现在看起来是不是好多了,我每次要变动颜色,只须要将此元素的 color 属性变动即可,不须要再重新写一堆重复的属性,当然,原生的 css 以及功能强大的 sass/less 都还是无法支持 rgba(currentColor, 0.1) 这种写法,我还辞官方提了个 issue ,官方也给了很好的回答,有兴趣的同学可以看看。

以是现在我只能添加一个 ::before 来仿照背景色块,真正做到只改 color 属性,即可改全部颜色。

现在大家就可以在 React 或 Vue 中通过状态来掌握改变颜色的类名添加与否并设置 color 属性,以此来完美地进行颜色的快速变换了~

多说一句,如果我们直策应用 ele.style.color = '#fff' 这种操作 dom 的形式来改变字体颜色,在未利用 currentColor 的情形下,我们是没法操作伪元素的,也就改变不了伪元素的 background 、 border-color 等其他与字体颜色同等的属性,以是这时候 currentColor 的上风就更明显了~

在线演示

利用变量 currentColor 减少重复代码 - codepen(https://codepen.io/vortesnail/pen/MWyzerK)

完美的带小箭头的谈天框需求描述主体功能聊景象泡,需有有边框 border 、背景色 background 、阴影、带边框的小三角箭头。
小三角的边框颜色和阴影颜色与主体框的颜色要同等,小三角的边框有 border-radius 。

考试测验方案

给该元素加个伪元素,背景色与谈天框背景色同等,再给该伪元素添加上、左同色边框,绝对定位调度位置,再来个 border-top-left-radius: 3px ,末了 transform: rotate(-45deg) 旋转一下,代码如下:

index.html 文件 :

<div class="chat-box">大家好,我是 vortesnail,如果大家喜好我的文章,对大家有所帮助,麻烦给个小小的赞支持一下,感激</div>复制代码

index.scss 文件 :

.chat-box { position: relative; max-width: 200px; padding: 10px; color: #faae43; background: #fff9ed; border: 1px solid #ffc16b; border-radius: 4px; box-shadow: 0 2px 6px rgba(250, 174, 67, 0.8);}.chat-box::before { position: absolute; top: 20px; left: -6px; width: 10px; height: 10px; background: #fff9ed; border-color: #ffc16b; border-style: solid; border-width: 1px 0 0 1px; transform: rotate(-45deg); content: ''; / box-shadow: 0 2px 6px rgba(250, 174, 67, 0.8); /}复制代码

可以达到现在下面的效果:

细心的你一定创造了,这个小三角指示箭头是没有阴影的,如果我给其加上与主体元素同等的 box-shadow ,又由于这个属性不能像 border-color 一样分别给各边设置为透明,结果就会像下面这样:

这已经是无法知足具有相同阴影的哀求了,而且大家如过想一下就知道,在我主体元素不设 padding 或设的很小的情形下,小三角的背景色会将我们的笔墨挡住,这种方案直接宣告失落败!

改进方案

针对以上的问题,我们进行一步步改造。

首先,我们考虑到主体元素不设置 padding 的情形,为了防止内容被我们的小三角背景色覆盖,我们可通过加一个伪元素 ::before ,利用 border 来画成一个三角形,代码如下:

.chat-box { // 其他样式 &::before { position: absolute; top: 20px; left: -8px; // 把稳,这里做了略微调度 width: 0;width height: 0; border-color: transparent #fff9ed transparent transparent; border-style: solid; border-width: 8px 8px 8px 0; content: ''; }}复制代码

现在是这个样子:

把稳,这里的小三角已经是没有右边部分的了,办理了我们不设置 padding 时导致内容被遮挡的问题。
但是这样就没有办法实现边框,毕竟你已经是利用边框做出来的三角形了。

那我们就再利用一个伪元素呗, ::after 安排上了。
接下来为大家供应一个思路:采取考试测验方案中的办法再画一个正方形做旋转,但是不为其设置背景色,只设置其 border ,调度下位置即可。

.chat-box { // 其他样式 &::before {} &::after { position: absolute; top: 22px; left: -7px; width: 10px; height: 10px; / border-color: inherit transparent transparent inherit; / border-color: transparent; border-style: solid; border-width: 1px; border-top-color: inherit; border-left-color: inherit; border-top-left-radius: 3px; transform: rotate(-45deg); content: ''; }}复制代码

可以看到,代码中我设置上和左的 border-color 为 inherit ,表示继续父级元素的 border-color ,因我注释那部分的写法不被识别,以是我们新增了几行代码实现,利用 inherit 可以在颜色变动时少写颜色值的重复代码,与 currentColor 想要达到的目的是同等的。

现在,越来越靠近我们的目标:

这里小三角还是没有阴影,由于 box-shadow 并不会浸染于伪元素,办理方案便是利用 filter 属性, drop-shadow 接管的参数和 box-shadow 基本同等,我们替代它即可:

// box-shadow: 0 2px 6px rgba(250, 174, 67, 0.8);box-shadowfilter: drop-shadow(0 2px 6px rgba(250, 174, 67, 0.8));复制代码

现在已经完美实现~

在线演示

实现一个完美的带小箭头的谈天框 - codepen(https://codepen.io/vortesnail/pen/mdPQgwY)

利用 grid 实现完美的水平铺满、间隔同等的自适应布局需求描述在一个容器元素下,有不愿定数量的子元素,哀求他们水平铺满,并且在当前行的最左边和最右边的子元素间隔父元素左边缘和右边缘都是无缝贴合的。
每个子元素之间的间隔必须同等。
当浏览器窗口大小变动自适应。

考试测验方案

这个问题从我入职第一份事情之后困扰了我靠近半年,我基本还是惯性思维,一眼看过去便是 flex弹性盒子 一把梭,于是我有了以下这种方案:

index.html 文件 :

<body> <div class="father"> <div class="child">Child1</div> <div class="child">Child2</div> <div class="child">Child3</div> <div class="child">Child4</div> <div class="child">Child5</div> <div class="child">Child6</div> <div class="child">Child7</div> <div class="child">Child8</div> <div class="child">Child9</div> <div class="child">Child10</div> </div></body>复制代码

index.scss 文件 :

.father { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: flex-start; width: 100%; padding: 10px 0 10px 20px; .child { margin-right: 14px; margin-bottom: 14px; // 其他卡片样式 }}复制代码

可以看到,我会为每个子元素都设置 margin-top 以及 margin-right 来固定他们之间的间距,但是由于每一行最右边的子元素也有 margin-right ,为了补偿这个,我就将父元素的 padding-right 去掉了,这样做的坏处太多了,须要自己去打算,做补偿,而且右边有时候容纳不下一个完全的子元素,就会导致换行而留下一大片白。

为了能用弹性盒子做到想要的效果,我已经把阮一峰老师的Flex 布局教程:语法篇看烂了。

根本没法实现最佳最想要的效果,以上只是我多次考试测验之后唯一能接管的方案,我就这么个方案用了好多次。

直到有一天,我又碰着了这种布局需求,我辛辛劳苦用 js 去硬算他们之间的间距,算是实现了想要的效果,但是真的非常繁琐,我就受不明晰。
这个时候我又偶遇了阮一峰老师的CSS Grid 网格布局教程,谢天谢地,采取 Grid 可完美实现以上需求!

改进方案

Flex 布局是轴线布局,只能指定"项目"针对轴线的位置,可以看作是一维布局。
Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局。
Grid 布局远比 Flex 布局强大。

首先我们须要给容器指定为 grid 网格布局,就像 flex 一样:

.father { display: grid;}复制代码

接着要为其划分列数, grid-template-columns 属性可定义每一列的列宽,如果代码如下,我们将容器划分成 3 列,每列宽度为容器的 100px :

.father { grid-template-columns: 100px 100px 100px;}复制代码

但是这个时候我们看到的效果会是下面这样:

子元素并没有把父元素占满,这显然不是我们想要的效果,幸亏有 repeat() 函数帮助我们简化重复值, 它接管两个参数,第一个参数是重复的次数,第二个参数是所要重复的值 。
上面的代码完备可用以下代码代替:

.father { grid-template-columns: repeat(3, 100px);}复制代码

当然,这只是第一步,我们还须要借助 auto-fill 关键字,在我们须要容器能尽可能容纳子元素时,就须要用到它,表示自动添补,我的理解是 repeat() 接管了这个 auto-fill 的参数时,会去自动打算容纳的数量,就彷佛你事先算出来这个容器能容纳多少子元素,然后把这个“多少”传给该函数一样。
这时候代码如下:

.father { display: grid; grid-template-columns: repeat(auto-fill, 100px);}复制代码

现在图形如下,已经越来越靠近我们的目标了:

但是很显然,右边有一个空隙, justify-content 属性拯救我们,它全体内容区域在容器里面的水平位置,当我设置其为 space-between 时,意味着子元素之间的间隔相等,而子元素与容器边框之间没有间隔。

不过子元素之间还是没有间隔,大略设置一下属性 gap 即可,它是 column-gap 和 row-gap 的合并简写,分别表示列与列和行与行之间的间距,现在代码如下:

.father { display: grid; grid-template-columns: repeat(auto-fill, 100px); justify-content: space-between; gap: 14px 4px;}复制代码

由此大略的几行代码就已经完美实现了我们想要的效果:

不过 grid 网格布局的兼容性不是很好,点此查看支持的浏览器列表~

在线演示

利用 grid 实现完美的水平铺满、间隔同等的自适应布局 - codepen(https://codepen.io/vortesnail/pen/NWNEmvx)

间距可调度的虚线框需求描述实现一个按钮,该按钮边框为虚线,且虚线的每个笔触之间的空隙和长度都是可调的。
能支持有圆角,即可设置 border-radius 。

考试测验方案

实在我一贯很迷惑为什么 css3 不供应一些能调度虚线框的必要属性,默认的 dash-border 常常会和 ui 所须要的虚线框哀求会不一致,既然官方不支持,我们只能自己探求一些办理方案。

这个办理方案看似很多,实在每一种办理方案都会有一定的局限性,选择最得当的便是最好的,详细我列出了以下几条:

利用 border-image 和自定义的图片来进行虚线框的天生,该方案在 stackoverflow 查到的,我个人也考试测验了下,但是修正起来特殊麻烦,我第一觉得便是不会采取这种方案,有兴趣的可以看下:Brew your own border with border-image。
在所须要虚线框的元素的宽高是固定的情形下,可以让 UI 画好这个虚线框就行,弊端很明显,长度若一旦发生变革,虚线比例和原来就不会同等,特殊丑。
利用 4 个绝对定位的“伪元素”来仿照,代码示例如下:

index.html 文件 :

<body> <div id="box"> <div class="border-horizontal top"></div> <div class="border-vertical right"></div> <div class="border-horizontal bottom"></div> <div class="border-vertical left"></div> I am vortesnail, now i try to make a custom dashed border! </div></body>复制代码

index.scss 文件 :

$border-color: #ccc;$border-dashed-unit-width: 8px;$border-dashed-unit-height: 1px;$stroke-rate: 50%;body { #box { width: 400px; background: #fff; padding: 10px; box-sizing: border-box; position: relative; .border-horizontal { position: absolute; width: 100%; height: $border-dashed-unit-height; left: 0; background-image: linear-gradient( to right, $border-color 0%, $border-color $stroke-rate, transparent $stroke-rate ); background-size: $border-dashed-unit-width $border-dashed-unit-height; background-repeat: repeat-x; } .border-vertical { position: absolute; width: $border-dashed-unit-height; height: 100%; top: 0; background-image: linear-gradient( to bottom, $border-color $stroke-rate, $border-color $stroke-rate, transparent $stroke-rate ); background-size: $border-dashed-unit-height $border-dashed-unit-width; background-repeat: repeat-y; } .top { top: 0; } .right { right: 0; } .bottom { bottom: 0; } .left { left: 0; } }}复制代码

实在其思想很大略,便是 4 个矩形,每个矩形加上渐变背景,并 repeat 即可仿照虚线效果,其间距、比例可根据我们设定的变量去调度。

但是它的弊端非常大,便是无法调度 border-radius ,即没有圆角!
这里向大家展示只是为了抛砖引玉,万一你有更好的想法,或者你不须要圆角,那就可以用这个方案。
效果如下:

改进方案

实在我们借助 svg 就能比较不错的实现自定义虚线框,如果不想自己写 svg 的朋友可以直接在这个网站进行调度和天生:Customize your CSS Border ,但如果你轻微理解一些 svg 的用法,也完备可以自己实现,如果你想理解一下,可阅读这篇文章:SVG入门—如何手写SVG

代码如下: index.html 文件 :

<body> <div id="box">I am vortesnail, now i try to draw a dashed border box.</div></body>复制代码

index.scss 文件 :

body { #box { width: 400px; border-radius: 4px; padding: 10px; background-image: url('data:image/svg+xml,\ <svg xmlns="http://www.w3.org/2000/svg">\ <rect width="100%" height="100%" rx="4" ry="4" style="stroke: black; stroke-width: 2px; fill: none; stroke-dasharray: 8px 5px; stroke-dashoffset: 10px;"/>\ </svg>'); }}复制代码

可通过 stroke-width 调度虚线框宽度, stroke-dasharray 调度比例及长度、间距, stroke-dashoffset 调度偏移值。

tips:svg 方案在一些比较老的安卓机上会不兼容,纵然在新的机型上,也会涌现一些表现差异,虽然在 web 端支持 svg 的浏览器上表现是正常的,但若考虑到移动端用户群体时,利用请慎重。

在线演示

实现一个间距可调的虚线框 - codepen(https://codepen.io/vortesnail/pen/ZEWPwey)

自定义复选框需求描述能够完备自定义复选框样式,就像定义一个普通的 div 元素一样。
disabled 状态复选框样式也可以自定义。

考试测验方案

很显然,当我们利用默认的 input.checkbox 方案时,是没有办法改变其样式的,而且在不同浏览器之间,其表现也不一致,代码如下:

index.html 文件 :

<body> <div class="checkbox-container"> <input type="checkbox" id="apple"> <label for="apple">苹果</label> </div> <div class="checkbox-container"> <input type="checkbox" id="banana" disabled> <label for="banana">喷鼻香蕉</label> </div> <div class="checkbox-container"> <input type="checkbox" id="watermelon"> <label for="watermelon">西瓜</label> </div></body>复制代码

index.scss 文件 :

.checkbox-container { display: flex; align-items: center; input[type='checkbox'] { & + label { color: #333; } } input[type='checkbox']:disabled { &+ label { color: #c6c6c6; } }}复制代码

在 chrome 下表现为:

在 firefox 下表现为:

在 safari 下与在 firefox 下同等。

如果任由这种情形的发生,你们 ui 可能会找产品经理和你打一架~

改进方案

我们可以在不改变上面考试测验方案中的 html 构造,只需 css 即可做到!
给每一个 label 标签添加一个伪元素 ::before 作为复选框!

首先,我们给这个伪元素添加必要样式,使其符合 ui 的设计:

input[type="checkbox"] { & + label { display: flex; align-items: center; } & + label::before { box-sizing: border-box; content: "\a0"; / 不换行空格 / width: 13px; height: 13px; margin-right: 4px; border-radius: 2px; border: 1px solid #333; }}复制代码

现在的拙劣效果如下:

我们创造,默认的复选框还是存在的,我们怎么做到将其隐蔽而不毁坏其可访问性呢(即不能利用 display: none )?

input[type="checkbox"] { position: absolute; clip: rect(0, 0, 0, 0); & + label {...} & + label::before {...}}复制代码

以上隐蔽的方案引用至 css揭秘151页

现在点击我们自定义的复选框是没有任何效果的,接下来借助 css 的相邻兄弟选择器对 checked 状态、disabled 状态分别设置样式即可:

input[type="checkbox"]:checked { & + label::before { background-color: #1890ff; background-image: url("https://s1.ax1x.com/2020/10/11/0cUbi4.png"); background-repeat: no-repeat; background-size: 100% 100%; border: none; }}input[type="checkbox"]:disabled { & + label { color: #868686; cursor: not-allowed; } & + label::before { border-color: #868686; }}复制代码

可以看到,经由此番处理后,我们可以完备自主地去设计复选框样式,比如我在上面选中时,呈现了一张对勾的图片: background-image: url("https://s1.ax1x.com/2020/10/11/0cUbi4.png"); ,这极大地方便了我们对其呈现形式的掌控。

除此之外,你还可以设置 input[type="checkbox"]:focus 时的样式哦,赶紧试试吧!

在线演示

自定义复选框 - codepen(https://codepen.io/vortesnail/pen/abZOBjx)

交互式图片比拟效果需求描述before 和 after 图片比拟效果,可拖过拖拽中间的竖形条状进行两张图片的宽度变革。

考试测验方案

css3 中引入了 resize 属性,该属性可以不通过 js 就可以改变设置该属性的元素的宽度 width ,大家一定利用过 textarea 标签把?那个右下角的可拖拽变动长宽的东西便是该属性的功劳。
实际上,所有标签都可以设置该属性!

于是,大略的几段代码就可以达到交互式图片比拟效果,虽然和我们想要实现的效果有点差异,但如果哀求不高的话,就采取它吧:

index.html 文件 :

<div class="image-slider"> <div class="before-container"> <img src="https://img3.doubanio.com/view/photo/l/public/p2622600072.webp" alt="before"> </div> <img src="https://img9.doubanio.com/view/photo/l/public/p2380745925.webp" alt="after"></div>复制代码

index.scss 文件 :

.image-slider { position: relative; img { display: block; width: 720px; user-select: none; } .before-container { position: absolute; top: 0; left: 0; width: 50%; max-width: 100%; / 防止容器宽度拉长至比图片还宽 / overflow: hidden; / 必须不可见 / resize: horizontal; / 授予水平宽度可拉伸功能 / &::before { position: absolute; right: 0; bottom: 0; width: 12px; height: 12px; background: linear-gradient(-45deg, #000 50%, transparent 0); background-clip: content-box; cursor: ew-resize; content: ''; } }}复制代码

我们利用一个伪元素 ::before 来对右下角的拉伸图标进行覆盖,以便于自定义样式,现在效果如下:

这个方案弊端便是右下角的可拖拽图标无法变动位置和大小,纵然我们利用伪元素去覆盖,但是和我们需求中所须要的效果也相差甚远,于是我们不得不借助 js 了!

改进方案

在上述方案中增加一个 span 标签用于画我们的拖拽竖条,紧接着按照上述方案先将两张照片的位置和大小调好:

index.html 文件 :

<body> <div class="image-slider"> <div class="before-container"> <img src="https://img3.doubanio.com/view/photo/l/public/p2622600072.webp" alt="before"> </div> <img src="https://img9.doubanio.com/view/photo/l/public/p2380745925.webp" alt="after"> <span class="handler"></span> </div></body>复制代码

index.scss 文件 :

body { .image-slider { position: relative; img { display: block; width: 520px; user-select: none; pointer-events: none; } .before-container { position: absolute; left: 0; top: 0; width: 50%; overflow: hidden; } }}复制代码

现在效果如下:

初步的效果出来了,接下来增加可拖拽改变水平宽度的功能。
首先须要先在两张图片交际出添加一个竖形的条状,用于拖拽位置,变动 class='handler' 样式:

.handler { position: absolute; top: 0; left: 50%; display: block; width: 4px; height: 100%; background: rgba(0, 0, 0, 0.4); transform: translateX(-50%); cursor: ew-resize;}复制代码

把稳中间的透明竖形条状即是我们可拖拽的位置:

接下来写我们的 js 脚本,首先通过原生 js 方法找到三个 dom 节点:

index.js 文件 :

const imageSlider = document.querySelector(".image-slider");const beforeContainer = document.querySelector(".before-container");const handler = document.querySelector(".handler");复制代码

然后我们还须要得到 image-slider 这个最外层元素相对页面左边的间隔,我们定义为变量 leftX ,并在鼠标于 handler 元素上按下时打算该值:

let leftX;handler.onmousedown = (e) => { leftX = e.pageX - handler.offsetLeft;};复制代码

用一张图来阐明解释下:

然后在给 window 工具添加一个 mousemove 的监听事宜,该回调用于改变 handler 位置和 before-image 的宽度:

handler.onmousedown = (e) => { leftX = e.pageX - handler.offsetLeft; window.addEventListener('mousemove', moveHandler)};const moveHandler = e => { const beforeWidth = e.pageX - leftX; const imageSliderWidth = imageSlider.offsetWidth; if (beforeWidth >= 0 && beforeWidth <= imageSliderWidth) { handler.style.left = beforeWidth + 'px'; beforeContainer.style.width = beforeWidth + 'px'; }}复制代码

目前为止,我们拖拽 handler 已经能实现所需的效果,但是无法停滞,我们须要添加一个鼠标按键抬起的监听事宜,须要把稳的是不能在 handler 上添加,比如: handler.onmouseup ,必须在 window 工具上添加,详细为什么,大家可以动手试试就知道了:

window.onmouseup = (e) => { window.removeEventListener('mousemove', moveHandler)};复制代码

到此为止,就已经完美实现了该效果,大家还可以在 handler 上做更多事情,使其用户体验达到更好~

在线演示

交互式图片比拟效果 - codepen(https://codepen.io/vortesnail/pen/wvWaJRB)

透明度渐变层代替滚动条提示需求描述一个可滚动列表,在未滚动到顶部之前,需有有一个渐变层代替滚动条作为剩余内容提示,底部同理;滚到到顶部时,渐变层消逝,底部同理;渐变层未矩形渐变。
考试测验方案

首先很明确的是,先将内容滚动条搞出来:

index.html 文件 :

<body> <div class="content-wrapper"> <header>目录</header> <div class="list-wrapper"> <ul> <li>如何长高</li> ...省略 </ul> </div> </div></body>复制代码

index.scss 文件 :

body { .content-wrapper { width: 248px; padding: 20px 0; background: #fafafa; header { padding: 0 20px 0 24px; font-weight: 500; font-size: 16px; } .list-wrapper { ul { height: 400px; padding: 0 20px 0 24px; overflow-y: auto; color: #595959; li { padding: 6px 0; font-size: 14px; list-style: none; } } } }}复制代码

现在效果如下:

接下来为滚动范围的顶部和底部都先加上我们所须要的渐变层,通过父容器的 ::before 和 ::after 伪元向来实现,同时动态为 list-wrapper 这个元素增加两个类名,用于掌握渐变层的显隐:

.list-wrapper { position: relative; &::before { position: absolute; right: 0; left: 0; z-index: 1; height: 60px; content: ''; pointer-events: none; } &.top-gradient::before { top: 0; background: linear-gradient(to bottom,#fafafa,hsla(0,0%,98%,.5) 84%,hsla(0,0%,98%,.13)); } &::after { position: absolute; right: 0; left: 0; z-index: 1; height: 60px; content: ''; pointer-events: none; } &.bottom-gradient::after { bottom: 0; background: linear-gradient(to top,#fafafa,hsla(0,0%,98%,.5) 84%,hsla(0,0%,98%,.13)); }}复制代码

但是我们想要达到的效果是:一旦滚动条不是最顶部,顶部就要有渐变层;一旦滚动条不是最底部,底部就要有渐变层。
现在完备是写去世在两头,须要通过大略的 js 脚本来判断 ul 元素的滚动条的位置:

index.js 文件 :

const listWrapper = document.querySelector(".list-wrapper");const ul = document.querySelector("ul");const onScroll = (e) => { // 滚动条是否在顶部 if (e.target.scrollTop > 0) { listWrapper.classList.add("top-gradient"); } else { listWrapper.classList.remove("top-gradient"); } // 滚动条是否在底部 if (e.target.scrollHeight - e.target.scrollTop !== ul.offsetHeight) { listWrapper.classList.add("bottom-gradient"); } else { listWrapper.classList.remove("bottom-gradient"); }};ul.addEventListener("scroll", onScroll);复制代码

末了再将原生滚动条隐蔽,OK!

ul { ... scrollbar-width: none;scrollbar-width / Firefox / -ms-overflow-style: none; / IE10+ / &::-webkit-scrollbar { display: none; / Chrome / }}复制代码

顺带说一句,在 codepen 中滚动条隐蔽不了,本地调试时可以,我也不晓得啥问题~

改进方案

实际上上述方案是我看《css揭秘》之后想到的,在这本书中,讲到了利用两层 background 以及 background-attachment 属性来进行渐变层的实现,但是我按书中实现之后,创造效果并不完美,乃至可以说有很大毛病!
我想了好久还是以为用 js 方便,css 看起来是无法实现我想要的效果的!

以是上述方案便是终极改进方案,《css揭秘》中的方法我实在不敢认同,不过关于 background-attachment 属性的先容倒是给我学到了~

在线演示

透明度渐变层代替滚动条提示 - codepen(https://codepen.io/vortesnail/pen/PozqKyM)

结语

虽然标题写了是 css3,但是还是难免涉及到了 js,我的目的是希望有同类需求的小伙伴能通过本篇文章得到帮助。
欢迎各位理性谈论~如果有更好的方法,请大佬们务必不吝见教!
如果你已经看到此处,干脆点个赞再走吧~

作者:vortesnail链接:https://juejin.cn/post/6882704719882485774来源:掘金