前两天在 Youtube 上看视频时,无意间发现视频播放器的播放暂停切换按钮动画十分流畅,于是就想试着用 CSS 仿制一下,先上效果图:

preview

等等,暂停按钮可以用border-leftborder-right实现,播放的三角形也可以用边框实现,这岂不是骗小孩子的把戏?于是乎没几分钟,我们就可以写出这么一个实现:

可是致命的缺陷是,由于暂停按钮中间缝隙的关系,我们必须对width元素进行动画处理,这直接导致border-width的动画无法按照我们设想的方式工作。于是乎,换个方向考虑,只能使用伪元素或者再添加一个div元素将两个边框分别实现了。

在这里为了布局方便,能够使用flex布局,使用两个div来实现,实际也可以只用一个div配合伪元素以及普通定位方式来获得更好的兼容性。

首先用两个div的边框模拟出暂停按钮的两个大长棍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<head>
<style>
body {
background-color: #333;
}
.wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin: 32px;
width: 32px;
height: 32px;
}
.box {
width: 0;
height: 24px;
border-left: 8px solid white;
}
.left {
margin-left: 6px;
}

.right {
margin-right: 6px;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box left"></div>
<div class="box right"></div>
</div>
</body>

然后为外层div添加一个click事件,在click事件触发时toggle两个子div元素的active类:

1
2
3
4
5
6
$div = document.querySelector(".wrapper");
$div.addEventListener("click", () => {
[...$div.children].forEach((element) => {
element.classList.toggle("active");
});
});

接下来我们就得定义这两根大棒在active状态下的属性了:左边的大棒在active状态下变换为梯形,右边的大棒在active状态下变换为三角形,且梯形的腰与三角形的两条边要在同一条直线上。这就要求border-left-widthborder-top-width的比值一定,即可以使用公共的border-width系列属性。在这里为了让上下边框也能受到transition属性的影响,在.box中也要定义初始值。

1
2
3
4
5
6
7
8
9
10
11
12
13
.box {
width: 0;
height: 24px;
transition: all 0.2s;
border-left: 8px solid white;
border-top: 0 solid transparent;
border-bottom: 0 solid transparent;
}
.box.active {
border-top: 6px solid transparent;
border-bottom: 6px solid transparent;
border-left: 10px solid white;
}

为了使右边的大棒在active状态下缩小为三角形,需要将其高度设置为0;为了使左边的大棒缩小为梯形,需要将其高度设置为短边的长度,即24px/2 = 12px

1
2
3
4
5
6
.right.active {
height: 0;
}
.left.active {
height: 12px;
}

到这里这个简单的 CSS 动画就完成啦,完成效果:

当然,如果要完全不使用JavaScript,则可以使用input复选框与label标签的:active伪类以及::before::after伪元素来实现,只不过语义性会有一定的缺失。并且,由于没有flexbox黑科技的支持,居中定位会变得麻烦一点。