CSS3和SVG实现的圆环菜单动画

<!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>

试一试

猜你喜欢

转载自blog.csdn.net/ithunzi/article/details/130412793
今日推荐