打造独特弧形边框选项卡:HTML、CSS 与 JavaScript 实现
一、最终效果展示
二、代码实现
(一)HTML 结构搭建
在 HTML 部分,创建一个基本的结构。<div class="root"> 作为整体容器,内部的 <ul class="tab"> 是选项卡的列表。每个选项卡是一个 <li> 元素,其中初始激活的选项卡添加了 active 类。为了避免文字随选项卡旋转而变形,我们在每个 <li> 中包裹了一个 <span> 标签来放置文本。代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>弧形边框选项卡</title>
<!-- 引入CSS样式 -->
<style>
/* 这里是CSS代码,稍后详细讲解 */
</style>
</head>
<body>
<div class="root">
<!-- 选项卡列表 -->
<ul class="tab">
<!-- 初始激活的选项卡 -->
<li class="active"><span>选项卡一</span></li>
<!-- 普通选项卡 -->
<li><span>选项卡二</span></li>
</ul>
</div>
<!-- 引入JavaScript脚本 -->
<script>
/* 这里是JavaScript代码,稍后详细讲解 */
</script>
</body>
</html>
(二)CSS 样式设计
全局样式重置
/* 全局样式重置,去除所有元素的默认外边距和内边距 */
* {
margin: 0;
padding: 0;
}
这一步确保所有浏览器对元素的默认样式设置一致,避免因浏览器差异导致的显示问题。
设置页面背景
body {
background-color: #6e6e6e;
}
这里将页面背景颜色设置为深灰色,与选项卡的颜色形成对比,突出选项卡。
选项卡容器样式
/* 选项卡容器样式 */
ul {
margin: 0 auto; /* 水平居中 */
width: 90%; /* 宽度为父元素的90% */
list-style-type: none; /* 去除列表项的默认样式 */
display: flex; /* 使用弹性布局 */
justify-content: center; /* 水平居中排列子元素 */
text-align: center; /* 文本居中 */
align-items: center; /* 垂直居中排列子元素 */
border-bottom: 1px solid #ffffff; /* 底部添加白色边框 */
}
通过 display: flex 将 <ul> 设置为弹性容器,使内部的 <li> 元素能够水平排列且居中。设置 width 为父元素的 90%,并通过 margin: 0 auto 实现水平居中。去除列表项默认样式,添加底部白色边框。
单个选项卡样式
/* 单个选项卡样式 */
li {
box-sizing: border-box; /* 盒模型计算方式,包含内边距和边框 */
background-color: #ffffff; /* 默认背景颜色 */
width: 200px; /* 宽度 */
height: 60px; /* 高度 */
font-size: 24px; /* 字体大小 */
line-height: 60px; /* 行高,使文字垂直居中 */
border-radius: 20px 20px 0 0; /* 顶部圆角 */
position: relative; /* 相对定位,为伪元素定位做准备 */
user-select: none; /* 禁止用户选择文本 */
transform: perspective(40px) rotateX(7deg); /* 3D透视旋转,使选项卡呈现弧形 */
transform-origin: center bottom; /* 旋转原点在底部中心 */
z-index: 1; /* 默认层级为1 */
}
每个选项卡 <li> 设置了固定的宽度、高度、字体大小和行高,确保文字垂直居中。通过 border-radius 设置顶部圆角,background-color 设置默认背景色为白色。利用 transform 和 transform-origin 属性实现 3D 透视旋转,使选项卡呈现独特的弧形效果。设置 position: relative,以便为后续的伪元素定位。user-select: none 禁止用户在选项卡上进行文本选择操作。z-index: 1 设置默认层级,确保选项卡正常显示。
选项卡左右两侧的伪元素样式
/* 选项卡左右两侧的伪元素,用于实现弧形效果 */
li::before, li::after {
content: ''; /* 伪元素内容为空 */
position: absolute; /* 绝对定位 */
width: 30px; /* 宽度 */
height: 30px; /* 高度 */
bottom: 0; /* 位于底部 */
}
/* 左侧伪元素 */
li::before {
left: -30px; /* 位于左侧 */
background: radial-gradient(circle at 0 0, transparent 30px, #ffffff 30px); /* 径向渐变,实现弧形背景 */
}
/* 右侧伪元素 */
li::after {
right: -30px; /* 位于右侧 */
background: radial-gradient(circle at 100% 0, transparent 30px, #ffffff 30px); /* 径向渐变,实现弧形背景 */
}
通过 ::before 和 ::after 伪元素,在选项卡的左右两侧添加了两个小的圆形区域,利用径向渐变实现与选项卡主体颜色一致的弧形背景,进一步增强选项卡的弧形视觉效果。
激活状态的选项卡样式
/* 激活状态的选项卡样式 */
li.active {
background-color: #ff6b6b; /* 激活状态背景颜色 */
z-index: 2; /* 激活状态层级为2,确保在最上方 */
}
/* 激活状态选项卡的左侧伪元素样式 */
li.active::before {
background: radial-gradient(circle at 0 0, transparent 30px, #ff6b6b 30px); /* 激活状态时的径向渐变背景 */
}
/* 激活状态选项卡的右侧伪元素样式 */
li.active::after {
background: radial-gradient(circle at 100% 0, transparent 30px, #ff6b6b 30px); /* 激活状态时的径向渐变背景 */
}
当选项卡处于激活状态(即被点击选中)时,通过 .active 类改变其背景颜色为醒目的橙色,并将 z-index 提升到 2,确保激活的选项卡显示在最上方。同时,相应地改变其左右两侧伪元素的背景颜色,以保持整体样式的一致性。
选项卡内文字样式
/* 选项卡内文字样式,通过反向旋转抵消选项卡的旋转,避免文字变形 */
li span {
display: inline-block; /* 行内块元素 */
transform: perspective(40px) rotateX(-7deg); /* 反向3D透视旋转 */
transform-origin: center bottom; /* 旋转原点在底部中心 */
}
由于选项卡整体进行了旋转,为了避免文字随之旋转而变形,对 <span> 标签内的文字应用了反向的 transform,使其保持正常显示。
(三)JavaScript 交互实现
// 获取选项卡列表元素
let tab = document.querySelector('.tab');
// 为选项卡列表添加点击事件监听器
tab.addEventListener('click', function (e) {
// 检查点击的元素是否为列表项
if (e.target.closest('li')) {
// 获取所有列表项
const lis = tab.querySelectorAll('li');
// 遍历所有列表项,移除激活类
lis.forEach(li => li.classList.remove('active'));
// 获取点击的列表项
const targetLi = e.target.closest('li');
// 为点击的列表项添加激活类
targetLi.classList.add('active');
}
});
在 JavaScript 部分,首先通过 document.querySelector('.tab') 获取到选项卡的列表元素。然后为该元素添加点击事件监听器,当用户点击选项卡列表时,判断点击的元素是否为 <li>。如果是,则获取所有的 <li> 元素,遍历并移除它们的 active 类,以取消之前激活的选项卡。接着获取当前点击的 <li> 元素,并为其添加 active 类,从而实现选项卡的切换效果。
三、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>弧形边框选项卡</title>
<style>
/* 全局样式重置,去除所有元素的默认外边距和内边距 */
* {
margin: 0;
padding: 0;
}
body {
background-color: #6e6e6e;
}
/* 选项卡容器样式 */
ul {
margin: 0 auto; /* 水平居中 */
width: 90%; /* 宽度为父元素的90% */
list-style-type: none; /* 去除列表项的默认样式 */
display: flex; /* 使用弹性布局 */
justify-content: center; /* 水平居中排列子元素 */
text-align: center; /* 文本居中 */
align-items: center; /* 垂直居中排列子元素 */
border-bottom: 1px solid #ffffff; /* 底部添加灰色边框 */
}
/* 单个选项卡样式 */
li {
box-sizing: border-box; /* 盒模型计算方式,包含内边距和边框 */
background-color: #ffffff; /* 默认背景颜色 */
width: 200px; /* 宽度 */
height: 60px; /* 高度 */
font-size: 24px; /* 字体大小 */
line-height: 60px; /* 行高,使文字垂直居中 */
border-radius: 20px 20px 0 0; /* 顶部圆角 */
position: relative; /* 相对定位,为伪元素定位做准备 */
user-select: none; /* 禁止用户选择文本 */
transform: perspective(40px) rotateX(7deg); /* 3D透视旋转,使选项卡呈现弧形 */
transform-origin: center bottom; /* 旋转原点在底部中心 */
z-index: 1; /* 默认层级为1 */
}
/* 选项卡左右两侧的伪元素,用于实现弧形效果 */
li::before, li::after {
content: ''; /* 伪元素内容为空 */
position: absolute; /* 绝对定位 */
width: 30px; /* 宽度 */
height: 30px; /* 高度 */
bottom: 0; /* 位于底部 */
}
/* 左侧伪元素 */
li:before {
left: -30px; /* 位于左侧 */
background: radial-gradient(circle at 0 0, transparent 30px, #ffffff 30px); /* 径向渐变,实现弧形背景 */
}
/* 右侧伪元素 */
li:after {
right: -30px; /* 位于右侧 */
background: radial-gradient(circle at 100% 0, transparent 30px, #ffffff 30px); /* 径向渐变,实现弧形背景 */
}
/* 激活状态的选项卡样式 */
li.active {
background-color: #ff6b6b; /* 激活状态背景颜色 */
z-index: 2; /* 激活状态层级为2,确保在最上方 */
}
/* 激活状态选项卡的左侧伪元素样式 */
li.active::before {
background: radial-gradient(circle at 0 0, transparent 30px, #ff6b6b 30px); /* 激活状态时的径向渐变背景 */
}
/* 激活状态选项卡的右侧伪元素样式 */
li.active::after {
background: radial-gradient(circle at 100% 0, transparent 30px, #ff6b6b 30px); /* 激活状态时的径向渐变背景 */
}
/* 选项卡内文字样式,通过反向旋转抵消选项卡的旋转,避免文字变形 */
li span {
display: inline-block; /* 行内块元素 */
transform: perspective(40px) rotateX(-7deg); /* 反向3D透视旋转 */
transform-origin: center bottom; /* 旋转原点在底部中心 */
}
</style>
</head>
<body>
<div class="root">
<!-- 选项卡列表 -->
<ul class="tab">
<!-- 初始激活的选项卡 -->
<li class="active"><span>选项卡一</span></li>
<!-- 普通选项卡 -->
<li><span>选项卡二</span></li>
</ul>
</div>
<script>
// 获取选项卡列表元素
let tab = document.querySelector('.tab');
// 为选项卡列表添加点击事件监听器
tab.addEventListener('click', function (e) {
// 检查点击的元素是否为列表项
if (e.target.closest('li')) {
// 获取所有列表项
const lis = tab.querySelectorAll('li');
// 遍历所有列表项,移除激活类
lis.forEach(li => li.classList.remove('active'));
// 获取点击的列表项
const targetLi = e.target.closest('li');
// 为点击的列表项添加激活类
targetLi.classList.add('active');
}
});
</script>
</body>
</html>