系列文章
说在前面
本篇是张鑫旭老师的 CSS基础测试6 的阅后笔记。(Wing Meng、XboxYan、Seasonley 同学的回答都很优雅)
题目
先来看看题目。
下图是 chrome 浏览器下 <audio> 播放器效果,请尝试使用纯 CSS 完成样式与布局效果(切换状态无需实现)。
需求:
- 布局自适应
- 尺寸和取色
- 纯 CSS 图标
- 代码友好
思路
说实话,这题目看上去还是有一定难度的,特别是通过纯 CSS 实现图标。
那么很明显,今天的重点就在这里了:纯 CSS 绘图。
播放按钮
在前面的 CSS 小测 - 02 中有简单提到过通过 border 来绘制三角形的方法,我们先画出来看看:
.triangle {
border: 14px solid transparent;
border-left-color: #000;
}
效果如下:
可以看到,这个三角形和 <audio> 中的三角形有一定的区别,这是一个等腰直角三角形,而 <audio> 中的三角形是一个等边三角形。
我们再来看看一个正方形的 border。
.triangle {
border-top: 80px solid deeppink;
border-left: 80px solid deepskyblue;
border-right: 80px solid yellowgreen;
border-bottom: 80px solid #000;
}
效果如下:
现在我们去掉其中一个 border,这里选择 border-right,再来看看效果:
到这里就比较明了了,只要让 border-left 的长度等于等腰三角形的高(由于这里的高是个无理数,只能取近似值),然后隐藏上下的 border即可。
.triangle {
border-top: 80px solid transparent;
border-bottom: 80px solid transparent;
border-left: 138.56px solid #000;
}
效果如下:
进度按钮
实现这样的进度条按钮,用 input[type="range"] 是非常好的,仅需要自己手动重置样式即可:
<input type="range" class="slidebar" />
.slidbar {
-webkit-appearance: none;
height: 0.25em;
font-size: 1em;
color: inherit;
background: currentColor;
border-radius: 0.25em;
}
.slidbar::-webkit-slider-thumb {
-webkit-appearance: none;
width: 0;
height: 0;
border: 0.375em solid;
border-radius: 50%;
box-shadow: 50vw 0 0 50vw rgba(255, 255, 255, 0.5);
}
效果如下:
声音按钮
纯 css 实现图标里,声音按钮应该是本题中最难的部分了。
开发这种不规则的图标,一般来说,以下几种技巧比较常用:
box-shadowlinear-gradient/radial-radientclip-path
其中由于 box-shadow 和 linear-gradient/radial-radient 可以无限叠加的特性,理论上来说可以实现任何图案。这里我们结合 box-shadow 和 clip-path 来实现声音按钮图标。
首先我们来绘制声音图标的这个喇叭,我们把它等比放大一下,相信看过前面通过 border 绘制三角形的同学应该对这个图形的绘制一目了然了。
没错,我们只需要一个块,然后让其只显示右边的 border 即可:
.volume::after {
content: "";
width: 4px;
height: 6px;
background: black;
border: 5px solid #fff;
border-right-color: black;
}
效果如下:
接下来我们通过 box-shadow 来实现右边的音量部分,同样,我们把这个部分等比放大来看看:
很明显,我们可以先画一个正圆,然后让通过 box-shadow inset 画出一个圆环:
.volume::before {
content: "";
background: black;
border-radius: 50%;
width: 180px;
height: 180px;
box-shadow: 0 0 0 20px black inset, 0 0 0 50px #fff inset;
}
效果如下:
接下来通过 clip-path 将左半边裁剪掉,再等比缩小回原来的大小,就实现了目标效果:
.volume::before {
content: "";
background: black;
border-radius: 50%;
width: 18px;
height: 18px;
clip-path: inset(0% 0% 0% 50%);
box-shadow: 0 0 0 2px black inset, 0 0 0 5px #fff inset;
}
效果如下:
最终将二者拼接起来即可:
<div class="box">
<div class="volume"></div>
</div>
.box {
line-height: 56px;
}
.volume::after {
content: "";
display: inline-block;
width: 4px;
height: 6px;
background: black;
border: 5px solid #fff;
border-right-color: black;
vertical-align: middle;
}
.volume::before {
content: "";
display: inline-block;
background: black;
border-radius: 50%;
width: 18px;
height: 18px;
position: absolute;
margin: 20px 0 0 7px;
clip-path: inset(0% 0% 0% 50%);
box-shadow: 0 0 0 2px black inset, 0 0 0 5px #fff inset;
}
效果如下:
菜单按钮
这个图标没有什么实现上的难度,思路是通过元素本身和两个伪类画三个小点,然后通过定位让其排列成目标状态即可:
<div class="menu"></div>
.menu {
width: 4px;
height: 4px;
background: black;
}
.menu::before,
.menu::after {
content: "";
width: 4px;
height: 4px;
background: black;
position: absolute;
}
.menu::before {
margin-top: -6px;
}
.menu::after {
margin-top: 6px;
}
效果如下:
播放容器
到这里,只需要把前面画的按钮拼接起来即可,当然,要注意命名规范,事实上,一开始就应该以模块化的思路进行开发,这里是为了尽量减少各个按钮之间的联系,方便学习思路清晰才从点到面进行开发,实际开发不应以此为例。
<div class="audio">
<a class="audio-play"></a>
<span class="audio-time" data-audio-time="now">0:05</span>
<span class="audio-time" data-audio-time="all">3:27</span>
<input class="audio-timeline" type="range" />
<a class="audio-volume"></a>
<a class="audio-menu"></a>
</div>
body {
margin: 0;
height: 100vh;
}
.audio {
box-sizing: border-box;
min-width: 330px;
height: 56px;
border-radius: 28px;
padding: 0 14px;
background: #f1f3f4;
color: black;
display: flex;
align-items: center;
}
.audio-play {
margin: 0 14px;
border-top: 7px solid transparent;
border-bottom: 7px solid transparent;
border-left: 11px solid #000;
}
.audio-time {
font-size: 15px;
}
.audio-time[data-audio-time="all"]::before {
content: "/";
margin: 0 4px;
}
.audio-timeline {
-webkit-appearance: none;
cursor: ew-resize;
height: 14px;
background: black;
border: 7px solid #f1f3f4;
overflow: hidden;
box-shadow: 0 0 0 5px #ffffff inset;
mix-blend-mode: darken;
flex-grow: 1;
}
.audio-timeline::-webkit-slider-thumb {
-webkit-appearance: none;
height: 0;
width: 0;
border-radius: 50%;
border: 6px solid black;
box-shadow: 50vw 0 0 50vw rgba(255, 255, 255, 0.5);
}
.audio-volume {
width: 4px;
height: 6px;
background: black;
border: 5px solid #f1f3f4;
border-right-color: black;
margin: 8px 20px 8px 0px;
}
.audio-volume::after {
content: "";
display: block;
background: black;
border-radius: 50%;
width: 18px;
height: 18px;
position: absolute;
clip-path: inset(0% 0% 0% 50%);
box-shadow: 0 0 0 2px black inset, 0 0 0 5px #f1f3f4 inset;
margin: -6px 0 0 2px;
mix-blend-mode: darken;
}
.audio-menu {
width: 4px;
height: 4px;
background: black;
border-radius: 50%;
border: 14px solid #f1f3f4;
}
.audio-menu::before,
.audio-menu::after {
content: "";
display: block;
width: 4px;
height: 4px;
background: black;
border-radius: 50%;
position: absolute;
}
.audio-menu::before {
margin-top: -6px;
}
.audio-menu::after {
margin-top: 6px;
}
效果如下:
优化
CSS 说起优化,首要考虑的都是用户体验,可以考虑键盘和辅助设备,被选中的时候给出提示。
首先重置按钮的 outline:
/* reset outline */
[class^="audio-"] {
outline: none;
}
然后将按钮的指针改变成手指:
/* reset cursor */
.audio-play,
.audio-volume,
.audio-menu {
cursor: pointer;
}
接下来为可以选中的按钮添加 hover 和 focus 效果即可:
/* play hover, focus */
.audio-play::after {
content: "";
display: block;
width: 32px;
height: 32px;
position: absolute;
border-radius: 50%;
margin: -16px 0 0 -23px;
}
.audio-play:hover::after,
.audio-play:focus::after {
background: rgba(0, 0, 0, 0.1);
}
/* timeline hover, focus */
.audio-timeline:focus,
.audio-timeline:hover {
outline: 4px solid rgba(0, 0, 0, 0.1);
outline-offset: -3px;
}
/* volume hover, focus */
.audio-volume::after {
content: "";
display: block;
width: 32px;
height: 32px;
position: absolute;
border-radius: 16px;
margin: -13px 0 0 -5px;
pointer-events: none;
}
.audio-volume:hover::after,
.audio-volume:focus::after {
background: rgba(0, 0, 0, 0.1);
}
/* menu hover, focus */
.audio-menu:focus,
.audio-menu:hover {
filter: brightness(0.9);
}
最终效果如下:
结束语
首先看看张老师总结的关键点:
- 自适应使用 flex 是比较好的方法,图标按钮尺寸固定,进度条宽度自适应;
- 进度条使用 input[type="range"] 是非常棒的实现与想法;
- 自适应不是等比例缩放,而是进度条宽度自适应;
- 注意视觉还原;
- 命名上注意上下文,例如 audio- 开头;
- CSS 图标尽快使用伪元素,更好维护;
- CSS 图标常用技巧:万能,box-shadow,linear-gradient/radial-radient,clip-path。
然后这里是 在线 demo