实现一个带S曲线的标签页切换效果

最近做项目遇到一个设计稿,标签页要做成这种带曲线的效果,折腾了半天终于搞定了,记录一下。

效果图

效果说明

就是那种选中的标签页会凸起来,边缘是S形曲线,看起来像是从背景里”挤”出来的感觉。

实现思路

核心就是用多层背景 + 伪元素来模拟这个效果。

HTML结构

1
2
3
4
5
<view class="tab-background"></view>
<view class="tab-active-bg" :class="activeTab === 'service' ? 'right' : 'left'"></view>
<view class="tab-no-active-bg-wrapper">
<view class="tab-no-active-bg" :class="activeTab === 'service' ? 'left' : 'right'"></view>
</view>

关键点1:选中标签比背景高

1
2
3
4
.tab-active-bg {
height: calc(100% + 16rpx);
top: -16rpx; /* 向上偏移16rpx,看起来就凸起了 */
}

关键点2:S曲线用伪元素 + 倾斜实现

1
2
3
4
5
.tab-active-bg.left::after {
content: '';
transform: skewX(15deg); /* 关键:15度倾斜 */
border-top-right-radius: 50rpx;
}

左右两边的曲线方向相反:

  • 左边用skewX(15deg)
  • 右边用skewX(-15deg)

关键点3:用wrapper防止伪元素溢出

1
2
3
4
.tab-no-active-bg-wrapper {
overflow: hidden; /* 这个很重要,不然伪元素会跑到外面去 */
border-radius: 40rpx 40rpx 0 0;
}

没有这个wrapper的话,那些倾斜的伪元素会超出边界,看起来很奇怪。

关键点4:box-shadow填补空隙

1
2
3
4
5
6
.tab-active-bg.left {
box-shadow: 65px 22px 0 #ffffff; /* 填补右边的空隙 */
}
.tab-active-bg.right {
box-shadow: -65px 20px 0 #ffffff; /* 填补左边的空隙 */
}

这个是最难调的,需要反复调整偏移值才能让曲线看起来自然。

踩过的坑

  1. 伪元素跑偏:一开始没用wrapper,伪元素到处乱跑
  2. 曲线不自然:box-shadow的偏移值调了好久
  3. 层级问题:忘记设置z-index,效果出不来

完整代码

就是开头贴的那些CSS,主要就是这几个要点:

  • 选中状态向上偏移16rpx
  • 用skewX(±15deg)做S曲线
  • wrapper设置overflow:hidden
  • box-shadow填补空隙
1
2
3
4
5
6
7
8
9
<!-- 标签背景 -->
<view class="tab-background"></view>
<!-- 活动标签背景 -->
<view class="tab-active-bg" :class="activeTab === 'service' ? 'right' : 'left'">
</view>
<view class="tab-no-active-bg-wrapper">
<view class="tab-no-active-bg" :class="activeTab === 'service' ? 'left' : 'right'">
</view>
</view>
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

.tab-background {
position: absolute;
width: 100%;
height: 100%;
background: #f5f5f5;
border-radius: 40rpx 40rpx 0 0;
}

.tab-active-bg{
position: absolute;
width: 42%;
height: calc(100% + 16rpx);
background: white;
top: -16rpx;
left: 0;
border-radius: 40rpx 0 0 0;
/* box-shadow: 0 -8rpx 40rpx rgba(0, 0, 0, 0.1); */
/* transition: all 0.4s cubic-bezier(0.4, 0.0, 0.2, 1); */
z-index: 1;
opacity: 1;
background: #ffffff;
}

.tab-active-bg.left {
box-shadow: 65px 22px 0 #ffffff;
border-radius: 40rpx 0 0 0;

}

.tab-active-bg.right {
left: 58%;
border-radius: 0 40rpx 0 0;
box-shadow: -65px 20px 0 #ffffff;
}

.tab-no-active-bg-wrapper {
height: 100%;
width: 100%;
background-color: #f5f5f5;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
border-radius: 40rpx 40rpx 0 0;
}

.tab-no-active-bg{
position: absolute;
width: 42%;
height: calc(100% + 16rpx);
background: #f5f5f5;
top: -16rpx;
border-radius: 0 40rpx 0 0;
/* box-shadow: 0 -8rpx 40rpx rgba(0, 0, 0, 0.1); */
/* transition: all 0.4s cubic-bezier(0.4, 0.0, 0.2, 1); */
z-index: 1;
}
.tab-no-active-bg.right {
left: 58%;
}
.tab-no-active-bg.left{
left: 0;
}

.tab-active-bg.left::after {
content: '';
position: absolute;
right: -60rpx;
bottom: 0;
width: 70rpx;
height: 100%;
background-color: #ffffff;
transform: skewX(15deg);
border-top-right-radius: 50rpx;
}

.tab-active-bg.right::before {
content: '';
position: absolute;
left: -60rpx;
bottom: 0;
width: 70rpx;
height: 100%;
background-color: #ffffff;
transform: skewX(-15deg);
border-top-left-radius: 50rpx;
}

.tab-no-active-bg::before {
content: '';
position: absolute;
left: -55rpx;
bottom: 0;
width: 70rpx;
height: 100%;
background-color: #f5f5f5;
transform: skewX(15deg);
border-bottom-left-radius: 50rpx;
}
.tab-no-active-bg.left::after {
content: '';
position: absolute;
right: -56rpx;
bottom: 0;
width: 70rpx;
height: 100%;
background-color: #f5f5f5;
transform: skewX(-15deg);
border-bottom-right-radius: 50rpx;
}

调好参数后效果还挺不错的,分享给有需要的同学。