实现动态扇形旋转切换效果,切换进度支持渐变效果
效果展示
在线示例 https://code.juejin.cn/pen/7425559403589271588
源码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.position-center {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
}
.container {
--height: 20vh;
--progress: 0;
width: 100%;
height: var(--height);
position: relative;
overflow: hidden;
.inner {
width: 200%;
height: calc(var(--height) * 2);
background-color: #2f2f2f;
border-radius: 50%;
overflow: hidden;
.circle {
width: calc(var(--height) * 6.5);
height: calc(var(--height) * 6.5);
border-radius: 50%;
}
.circle-bottom {
bottom: 12%;
overflow: hidden;
padding: 25% 15% 0 15%;
background-color: #535353;
.circle-mask {
width: calc(var(--progress) * 1%);
height: 100%;
background-image: linear-gradient(to right, rgba(31, 231, 236, .3), rgba(31, 231, 236, .7));
transition: all .3s ease-in-out;
}
}
.circle-top {
background-color: #2f2f2f;
bottom: 13%;
padding: 27% 15% 0 15%;
color: #fff;
display: flex;
justify-content: space-around;
align-items: flex-end;
}
.circle-main {
width: calc(var(--height) * 6.5);
height: calc(var(--height) * 6.5);
border-radius: 50%;
transition: all .3s ease-in-out;
transform: translateX(-50%) rotate(0deg);
.item {
--rotate: 0;
position: absolute;
height: 100%;
display: flex;
justify-content: center;
align-items: flex-end;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(calc(var(--rotate) * -1deg));
.item-inner {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
bottom: -30px;
font-size: 14px;
color: #ccc;
.point {
width: 7px;
height: 7px;
background-color: #fff;
border-radius: 50%;
margin-top: 4px;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
&::before {
content: '';
width: 12px;
height: 12px;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.label-bottom {
margin-top: 5px;
}
}
.active {
.point {
background-color: rgba(31, 231, 236, 1);
&::before {
background-color: rgba(31, 231, 236, 0.3);
}
}
}
}
}
}
}
.btns {
position: absolute;
bottom: 500px;
left: 50%;
transform: translateX(-50%);
button {
color: #1fe7ec;
border: 1px solid #1fe7ec;
background-color: transparent;
padding: 4px 15px;
border-radius: 4px;
font-size: 14px;
}
}
</style>
</head>
<body>
<div id="container" class="container" style="--progress: 33.33">
<div class="inner position-center">
<div class="circle circle-bottom position-center">
<div class="circle-mask"></div>
</div>
<div class="circle circle-top position-center">
<div id="circle" class="circle-main position-center">
<div class="item" style="--rotate: -15;">
<div class="item-inner active">
<div class="label-top">10-15w</div>
<div class="point"></div>
<div class="label-bottom">旅行家 V1</div>
</div>
</div>
<div class="item" style="--rotate: 0;">
<div class="item-inner">
<div class="label-top">15-20w</div>
<div class="point"></div>
<div class="label-bottom">旅行家 V2</div>
</div>
</div>
<div class="item" style="--rotate: 15;">
<div class="item-inner">
<div class="label-top">20w+</div>
<div class="point"></div>
<div class="label-bottom">旅行家 V3</div>
</div>
</div>
<div class="item" style="--rotate: 30;">
<div class="item-inner">
<div class="label-top">30w+</div>
<div class="point"></div>
<div class="label-bottom">旅行家 V4</div>
</div>
</div>
<div class="item" style="--rotate: 45;">
<div class="item-inner">
<div class="label-top">50w+</div>
<div class="point"></div>
<div class="label-bottom">旅行家 V5</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="btns">
<button onclick="prev()">上一个</button>
<button onclick="next()">下一个</button>
</div>
<script>
const container = document.getElementById('container')
const circle = document.getElementById('circle')
const max = circle.children.length
let currentIndex = 0
const acitve = () => {
const items = circle.querySelectorAll('.item')
items.forEach((item, index) => {
const itemInner = item.querySelector('.item-inner')
if (index === currentIndex) {
itemInner.classList.add('active')
} else {
itemInner.classList.remove('active')
}
})
}
const next = () => {
if (currentIndex < max - 1) {
currentIndex += 1
}
if (currentIndex < max - 1) {
container.style.setProperty('--progress', 50)
circle.style.transform = `translateX(-50%) rotate(${15 * (currentIndex - 1)}deg)`
} else {
container.style.setProperty('--progress', 100)
}
acitve()
}
const prev = () => {
if (currentIndex > 0) {
currentIndex -= 1
}
if (currentIndex > 0) {
container.style.setProperty('--progress', 50)
circle.style.transform = `translateX(-50%) rotate(${15 * (currentIndex - 1)}deg)`
} else {
container.style.setProperty('--progress', 33.33)
}
acitve()
}
</script>
</body>
</html>