<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS3和SVG实现的圆环菜单动画</title>
<link href="https://fonts.googleapis.com/css?family=Poppins:200,400" rel="stylesheet"> <!--核心-->
<style>
@charset "UTF-8";
* {
margin: 0;
padding: 0;
border: 0;
outline: none;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
html,
body {
height: 100vh;
width: 100vw;
background-color: #000;
}
.main {
display: flex;
height: 100vh;
width: 100vw;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-items: center;
}
.navigation-circle {
display: block;
position: relative;
height: 320px;
width: 320px;
margin: auto;
}
.navigation-circle__inner {
display: block;
position: relative;
height: 100%;
width: 100%;
}
.navigation-circle__list {
display: block;
position: absolute;
height: 320px;
width: 320px;
transform: rotate(-90deg);
animation: 2.2s cubic-bezier(0.25, -0.25, 0.35, 1) 0 1 animate-in-list forwards;
}
.navigation-circle-svg {
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: rotateZ(-90deg);
}
.navigation-circle-svg--opaque {
opacity: 0.5;
}
.navigation-circle-svg--mask circle {
transition: all 0.5s ease;
transition-delay: 0.5s;
animation: 2.2s ease 0 1 animate-in-svg-circle-mask backwards;
}
.navigation-circle-list-item {
display: block;
position: absolute;
height: 0;
width: calc(50% + 16px);
top: 50%;
left: 50%;
list-style: none;
transform-origin: 0 0;
}
.navigation-circle-list-item__point {
display: block;
position: absolute;
height: 32px;
width: 32px;
top: -16px;
right: 2px;
cursor: pointer;
transform: scale(0);
}
.navigation-circle-list-item__point:before {
content: "•";
display: block;
position: absolute;
height: 32px;
width: 32px;
top: 0;
color: #c644fc;
background-color: #000;
font-size: 12px;
font-weight: 400;
line-height: 32px;
text-align: center;
border: 2px solid #c644fc;
border-radius: 50%;
box-shadow: inset 0px 0px 0px 0px #c644fc;
transform: scale(0.75);
transition: all 0.5s ease;
}
.navigation-circle-list-item__point:after {
content: "";
display: block;
position: absolute;
height: 1px;
width: 0px;
top: 18px;
left: 31px;
background-color: #c644fc;
transition: all 0.5s ease;
}
.navigation-circle-list-item__meta {
display: block;
position: absolute;
overflow: hidden;
opacity: 0;
transform-origin: center;
margin-left: 78px;
min-width: 128px;
padding: 4px;
}
.navigation-circle-list-item__title {
display: block;
color: #f7f7f7;
text-align: left;
font-size: 14px;
border-bottom: 1px solid #f7f7f7;
padding-bottom: 4px;
margin-bottom: 6px;
}
.navigation-circle-list-item__subtitle {
display: block;
color: #f7f7f7;
text-align: center;
font-weight: 200;
font-size: 12px;
}
.navigation-circle-list-item:nth-of-type(1) {
transform: rotateZ(calc((360deg / 7) * 1));
}
.navigation-circle-list-item:nth-of-type(1) .navigation-circle-list-item__meta {
transform: rotateZ(38.5714285714deg);
}
.navigation-circle-list-item:nth-of-type(1) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 0s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(2) {
transform: rotateZ(calc((360deg / 7) * 2));
}
.navigation-circle-list-item:nth-of-type(2) .navigation-circle-list-item__meta {
transform: rotateZ(-12.8571428571deg);
}
.navigation-circle-list-item:nth-of-type(2) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 0.2s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(3) {
transform: rotateZ(calc((360deg / 7) * 3));
}
.navigation-circle-list-item:nth-of-type(3) .navigation-circle-list-item__meta {
transform: rotateZ(-64.2857142857deg);
}
.navigation-circle-list-item:nth-of-type(3) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 0.4s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(4) {
transform: rotateZ(calc((360deg / 7) * 4));
}
.navigation-circle-list-item:nth-of-type(4) .navigation-circle-list-item__meta {
transform: rotateZ(-115.7142857143deg);
}
.navigation-circle-list-item:nth-of-type(4) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 0.6s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(5) {
transform: rotateZ(calc((360deg / 7) * 5));
}
.navigation-circle-list-item:nth-of-type(5) .navigation-circle-list-item__meta {
transform: rotateZ(-167.1428571429deg);
}
.navigation-circle-list-item:nth-of-type(5) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 0.8s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(6) {
transform: rotateZ(calc((360deg / 7) * 6));
}
.navigation-circle-list-item:nth-of-type(6) .navigation-circle-list-item__meta {
transform: rotateZ(-218.5714285714deg);
}
.navigation-circle-list-item:nth-of-type(6) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 1s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:nth-of-type(7) {
transform: rotateZ(calc((360deg / 7) * 7));
}
.navigation-circle-list-item:nth-of-type(7) .navigation-circle-list-item__meta {
transform: rotateZ(-270deg);
}
.navigation-circle-list-item:nth-of-type(7) .navigation-circle-list-item__point {
animation: 1s cubic-bezier(0.55, -0.3, 0.6, 1.5) 1.2s 1 animate-in-list-item-point forwards;
}
.navigation-circle-list-item:hover .navigation-circle-list-item__point:before {
transform: scale(1);
font-size: 16px;
}
.navigation-circle-list-item:hover .navigation-circle-list-item__point:after {
width: 32px;
left: 34px;
}
.navigation-circle-list-item:hover .navigation-circle-list-item__meta {
opacity: 1;
}
.navigation-circle-list-item:active .navigation-circle-list-item__point:before,
.navigation-circle-list-item.active .navigation-circle-list-item__point:before {
transform: scale(0.85);
color: #000;
box-shadow: inset 0px 0px 0px 16px #c644fc;
border-color: transparent;
}
.navigation-circle-list-item:active .navigation-circle-list-item__point:after,
.navigation-circle-list-item.active .navigation-circle-list-item__point:after {
width: 34px;
left: 32px;
}
.navigation-circle-list-item:active .navigation-circle-list-item__meta,
.navigation-circle-list-item.active .navigation-circle-list-item__meta {
opacity: 1;
}
@keyframes animate-in-list {
0% {
transform: rotate(-540deg);
}
100% {
transform: rotate(-90deg);
}
}
@keyframes animate-in-svg-circle-mask {
0% {
stroke-dashoffset: 1005;
}
100% {
stroke-dashoffset: 0;
}
}
@keyframes animate-in-list-item-point {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
</style>
</head>
<body>
<div class="main">
<div class="navigation-circle">
<div class="navigation-circle__inner"> <svg class="navigation-circle-svg navigation-circle-svg--opaque"
version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewbox="0 0 320 320"
style="enable-background:new 0 0 320 320;">
<circle cx="160" cy="160" r="158" fill="none" stroke-width="1" stroke="#c644fc" stroke-linecap="round"
stroke-miterlimit="10" style="stroke-dashoffset:0;stroke-dasharray:none;"></circle>
</svg> <svg class="navigation-circle-svg navigation-circle-svg--mask" version="1.1"
xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewbox="0 0 320 320"
style="enable-background:new 0 0 320 320;">
<circle id="mask-circle" cx="160" cy="160" r="158" fill="none" stroke-width="2" stroke="#c644fc"
stroke-linecap="round" stroke-miterlimit="10" style="stroke-dasharray:1005.3088px;"></circle>
</svg>
<ul class="navigation-circle__list">
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(1)"
onmouseenter="calculateOffset(1)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #1 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(2)"
onmouseenter="calculateOffset(2)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #2 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(3)"
onmouseenter="calculateOffset(3)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #3 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(4)"
onmouseenter="calculateOffset(4)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #4 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(5)"
onmouseenter="calculateOffset(5)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #5 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(6)"
onmouseenter="calculateOffset(6)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #6 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
<li class="navigation-circle-list-item"><a class="navigation-circle-list-item__point" onClick="onClick(7)"
onmouseenter="calculateOffset(7)" onMouseLeave="onMouseLeave()">
<div class="navigation-circle-list-item__meta">
<h3 class="navigation-circle-list-item__title">Item #7 </h3>
<h4 class="navigation-circle-list-item__subtitle">It just goes round and round</h4>
</div>
</a></li>
</ul>
</div>
</div>
</div>
<script>
const pointCount = 7;
const circleRadius = 160;
const startAnimDelta = 5;
const circumference = Math.PI * circleRadius * 2;
var selectedItemIndex = -1;
var circlePath = document.getElementById("mask-circle");
/**
* @description On Mouse Leave event handler for points
*/
const onMouseLeave = () => {
let index = selectedItemIndex !== -1 ? selectedItemIndex : 0;
calculateOffset(index);
};
/**
* @description On Click event handler for points
* @param {Number} index - Index of list item
*/
const onClick = (index) => {
//If already selected, deselect
selectedItemIndex = selectedItemIndex === index ? -1 : index;
calculateOffset(index);
//Find active item, deselect
let activeListItem = document.querySelectorAll(
".navigation-circle-list-item.active"
);
if (activeListItem.length > 0) activeListItem[0].classList.remove("active");
//Find new item by index, select
let listItem = document.querySelectorAll(
".navigation-circle-list-item:nth-of-type(" + selectedItemIndex + ")"
);
if (listItem.length > 0) listItem[0].classList.add("active");
};
/**
* @description - Calculate offset for circle path by index of list item
* @param {Number} index - Index of list item
*/
const calculateOffset = (index = 0) => {
let offset = 0;
if (index !== 0) offset = (circumference / pointCount) * (pointCount - index);
circlePath.style.strokeDashoffset = `${offset}px`;
};
// INTRO
let buffer = 500;
let delay =
1000 * (1 + pointCount / startAnimDelta - 1 / startAnimDelta) + buffer;
setTimeout(() => onClick(1), delay);
</script>
</body>
</html>
12-05
198

10-20
3892
