实现一个带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; }
|
关键点2:S曲线用伪元素 + 倾斜实现
1 2 3 4 5
| .tab-active-bg.left::after { content: ''; transform: skewX(15deg); 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; }
|
这个是最难调的,需要反复调整偏移值才能让曲线看起来自然。
踩过的坑
- 伪元素跑偏:一开始没用wrapper,伪元素到处乱跑
- 曲线不自然:box-shadow的偏移值调了好久
- 层级问题:忘记设置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; 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; 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; }
|
调好参数后效果还挺不错的,分享给有需要的同学。