总结7个工作中常用的css3案例,带你了解冷门却实用的特性
myzbx 2025-01-05 18:59 16 浏览
使用变量 currentColor 减少重复代码
需求描述
- 移到按钮上时,改变该元素的 border-color 、 color ,还有一个具有透明度的同色背景。
- 点击按钮之后,颜色更改为移入按钮时的同种颜色。
尝试方案
我相信任何一个前端开发者都能很快实现这个需求,不知道大家怎么样的,我在之前一直都是以下代码快速实现:
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 文件 :
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-shadow
filter: 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
来源:掘金
相关推荐
- 为什么钟表的指针是从左向右顺时针转?
-
所有的钟表指针都是从左向右转的,所以我们就用它来表示旋转方向了。那么,为什么钟表都是从左向右转呢?正着转也好,反着转也好,一圈不都是12小时吗?这就要从钟表的前身说起了。在钟表出现之前,人们使用过一种...
- 牛人将电子钟改造高精度时钟,日误差0.26秒!解决走时不准通病
-
家里有好多个电子钟,精度各种参差不齐,然后走时就是各种混乱,是可忍孰不可忍……自打发现8025这个好玩意儿之后,就决定不忍了。第一个上场的聪明钟,为啥叫聪明钟然后还走的不准。三节电池供电,其中3V给主...
- 篮球裁判手势图解之计时钟、得分替换和暂停手势
-
▋篮球裁判手势图解之计时钟手势停止计时钟手势,伸开手掌,垂直举过头部。犯规停止计时钟手势,一拳握紧,垂直举过头部。计时开始手势,用手做劈柴动作,将垂直举过头部的手放下。▋篮球裁判手势图解之得分手势1...
- 罗马数字的起源与用途
-
一、罗马数字的诞生与进化罗马数字起源于古罗马帝国,拥有一个漫长而复杂的历史,始于公元前8世纪至9世纪,与古罗马帝国在帕兰丁山(PalantineHill)周围建立的时间大致相同。不过,罗马数...
- 基于 Arduino Nano R3 的红外遥控数字时钟
-
由于在ArduinoNano上没有足够的引脚来编写代码,该项目只有有限的功能(即使没有设置时间的设施)。通过添加红外线遥控器,我可以灵活地整合所有需要的功能(如果需要,可能会更多),不需要额外的...
- 大班必备33首数字歌,轻松学数学
-
适合大班宝贝的33首数学歌,让孩子们在玩中学,通过好玩、好记的的儿歌来了解数学的知识点,轻松学数学!以上所有有关数学概念的知识点,其中包含了钟表、点数、分解组成、加减、单双数、倒数正数、凑十、方位...
- 11的寓意和象征
-
在数字的王国里,每个数字都有其独特的内涵和象征意义。今天,我们将一起探索数字11的奥秘和象征意义。这个奇特的数字,不仅在我们的日常生活中扮演着重要的角色,而且在神秘主义和宗教中也占有的一席之地。首先,...
- 基于TM1637的数字时钟
-
方案介绍这个项目是一个原型,我将在我正在进行的其他数字时钟项目中使用。这是我计划在我的下一个数字时钟项目中使用的时间和闹钟设置机制的原型。我希望能给你提供到帮助。如果你想到任何改进,请告诉我。我会更乐...
- 【金龟子讲睡前故事】数字不见啦
-
“快做数学题!”妈妈大声吼邦邦。“啊,好烦呀!”邦邦回到屋里,对着数学练习册大声嚷嚷。考拉熊博士在邦邦的屋外听到邦邦的声音,自言自语说:“好像又在发脾气,我得去看看他。”考拉熊博士推门进去,只见邦邦大...
- SE 最终幻想 35 周年,《FF7 重制版》破坏剑数字时钟 9 月发售
-
IT之家3月9日消息,SE今日正式开设了《最终幻想》35周年纪念网站,天野喜孝绘制官方LOGO公布!值得一提的是,索尼PlayStation游戏发布会即将于北京时间3月10...
- 谁说数字钟就是黑白状?他们让你改变看法
-
如果我们没有了钟表,你会用什么衡量时间?是利用太阳的变化还是凭猜测?之前设计癖也介绍一些有趣的钟表,像是Edelkrone设计的无表针的Oqloq钟表,也有淡化了表针概念的轨道钟表,今天再给大...
- 杭州元宵游玩大赏|“人体时钟”亮相文三数字生活街区,还有元宵巡游活动等你嗨
-
钱江晚报·小时新闻记者方力通讯员冯晨晨刘静滴答滴答,在这个时钟里面有一位虚拟的“小姐姐”。她的工作内容就是不断地把分针擦掉,然后再画上新的分针,她每画一次分针擦干净后,再画上一条新的分针,就刚...
- 来用PPT做一只数字时钟动画
-
“什么是可见性?“可见性”即指PPT动画元素中的一种。在我们之前的图文教程《动画基础扫盲课,必修!》中提到过一些常用的PPT动画元素。分别为可见性,X,Y坐标,旋转,高度和宽度。而其中所谓的“可见性”...
- 苹果手机桌面时钟怎么显示 苹果手机桌面时钟显示操作
-
苹果手机系统流畅,系统使用起来很舒适,是很多人的首选。苹果时钟可以在桌面上显示数字时钟,如果在编辑主屏幕时,不小心把时钟删掉了,要怎么恢复呢?或者想要设置时钟显示,操作是什么样的呢?苹果手机桌面时钟怎...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 选择器 (30)
- CSS 轮廓 (30)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 中级教程 (30)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)