HOME  >   CSS3 ANIMATION  >   How Does CSS3 and SVG Implement Circle Menu Animation
How Does CSS3 and SVG Implement Circle Menu Animation
BY Timothy13 Nov,2020
Tags:

The styles of web menus are truly diverse, especially the popularity of HTML5 and CSS3, making the appearance of web menus more colorful. Today we are going to share with you a ring-shaped menu based on CSS3 and SVG. There are 7 nodes on the ring, which represent menu items. When the mouse rolls over or clicks on one of the nodes, a text menu item will pop up. The ring shape and nodes are drawn using SVG, and the animation effect is also very good.

Advertisement

How Does CSS3 and SVG Implement Circle Menu Animation
HTML Body Code
<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>                        
Css Code
.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;
}                        
Js Code
const pointCount = 7;
const circleRadius = 160;
const startAnimDelta = 5;
const circumference = Math.PI * circleRadius * 2;

var selectedItemIndex = -1;

var circlePath = document.getElementById('mask-circle');

const onMouseLeave = () => {
	let index = (selectedItemIndex !== -1) ? selectedItemIndex : 0;
	calculateOffset(index);
};

const onClick = (index) => {

	selectedItemIndex = (selectedItemIndex === index) ? -1 : index;
	calculateOffset(index);
	
	let activeListItem = document.querySelectorAll('.navigation-circle-list-item.active');
	if (activeListItem.length > 0) activeListItem[0].classList.remove('active');
	
	let listItem = document.querySelectorAll('.navigation-circle-list-item:nth-of-type(' + selectedItemIndex + ')');
	if (listItem.length > 0) listItem[0].classList.add('active');
};

const calculateOffset = (index=0) => {
	let offset = 0;

	if (index !== 0) offset = (circumference / pointCount) * (pointCount - index);
	
	circlePath.style.strokeDashoffset = `${offset}px`;
};


let buffer = 500;
let delay = 1000 * (1 + (pointCount / startAnimDelta) - (1 / startAnimDelta)) + buffer;

setTimeout(() => onClick(1), delay);                        

Advertisement