先看效果: 在这里插入图片描述 再看代码:

 	<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>点赞</title>
  <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500&amp;display=swap" type="text/css" rel="stylesheet">
  <style>
    * {
      border: 0;
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    :root {
      --bg: #e3e4e8;
      --fg: #17181c;
      --primary: #255ff4;
      --yellow: #f4a825;
      --yellow-t: rgba(244, 168, 37, 0);
      --bezier: cubic-bezier(0.42,0,0.58,1);
      --trans-dur: 0.3s;
      font-size: calc(24px + (30 - 24) * (100vw - 320px) / (1280 - 320));
    }

    body {
      background-color: var(--bg);
      color: var(--fg);
      font: 1em/1.5 "DM Sans", sans-serif;
      display: flex;
      height: 100vh;
      transition: background-color var(--trans-dur), color var(--trans-dur);
    }

    .rating {
      margin: auto;
    }
    .rating__display {
      font-size: 1em;
      font-weight: 500;
      min-height: 1.25em;
      position: absolute;
      top: 100%;
      width: 100%;
      text-align: center;
    }
    .rating__stars {
      display: flex;
      padding-bottom: 0.375em;
      position: relative;
    }
    .rating__star {
      display: block;
      overflow: visible;
      pointer-events: none;
      width: 2em;
      height: 2em;
    }
    .rating__star-ring, .rating__star-fill, .rating__star-line, .rating__star-stroke {
      animation-duration: 1s;
      animation-timing-function: ease-in-out;
      animation-fill-mode: forwards;
    }
    .rating__star-ring, .rating__star-fill, .rating__star-line {
      stroke: var(--yellow);
    }
    .rating__star-fill {
      fill: var(--yellow);
      transform: scale(0);
      transition: fill var(--trans-dur) var(--bezier), transform var(--trans-dur) var(--bezier);
    }
    .rating__star-stroke {
      stroke: #c7cad1;
      transition: stroke var(--trans-dur);
    }
    .rating__label {
      cursor: pointer;
      padding: 0.125em;
    }
    .rating__label--delay1 .rating__star-ring, .rating__label--delay1 .rating__star-fill, .rating__label--delay1 .rating__star-line, .rating__label--delay1 .rating__star-stroke {
      animation-delay: 0.05s;
    }
    .rating__label--delay2 .rating__star-ring, .rating__label--delay2 .rating__star-fill, .rating__label--delay2 .rating__star-line, .rating__label--delay2 .rating__star-stroke {
      animation-delay: 0.1s;
    }
    .rating__label--delay3 .rating__star-ring, .rating__label--delay3 .rating__star-fill, .rating__label--delay3 .rating__star-line, .rating__label--delay3 .rating__star-stroke {
      animation-delay: 0.15s;
    }
    .rating__label--delay4 .rating__star-ring, .rating__label--delay4 .rating__star-fill, .rating__label--delay4 .rating__star-line, .rating__label--delay4 .rating__star-stroke {
      animation-delay: 0.2s;
    }
    .rating__input {
      -webkit-appearance: none;
      appearance: none;
    }
    .rating__input:hover ~ [data-rating]:not([hidden]) {
      display: none;
    }
    .rating__input-1:hover ~ [data-rating="1"][hidden], .rating__input-2:hover ~ [data-rating="2"][hidden], .rating__input-3:hover ~ [data-rating="3"][hidden], .rating__input-4:hover ~ [data-rating="4"][hidden], .rating__input-5:hover ~ [data-rating="5"][hidden], .rating__input:checked:hover ~ [data-rating]:not([hidden]) {
      display: block;
    }
    .rating__input-1:hover ~ .rating__label:first-of-type .rating__star-stroke, .rating__input-2:hover ~ .rating__label:nth-of-type(-n + 2) .rating__star-stroke, .rating__input-3:hover ~ .rating__label:nth-of-type(-n + 3) .rating__star-stroke, .rating__input-4:hover ~ .rating__label:nth-of-type(-n + 4) .rating__star-stroke, .rating__input-5:hover ~ .rating__label:nth-of-type(-n + 5) .rating__star-stroke {
      stroke: var(--yellow);
      transform: scale(1);
    }
    .rating__input-1:checked ~ .rating__label:first-of-type .rating__star-ring, .rating__input-2:checked ~ .rating__label:nth-of-type(-n + 2) .rating__star-ring, .rating__input-3:checked ~ .rating__label:nth-of-type(-n + 3) .rating__star-ring, .rating__input-4:checked ~ .rating__label:nth-of-type(-n + 4) .rating__star-ring, .rating__input-5:checked ~ .rating__label:nth-of-type(-n + 5) .rating__star-ring {
      animation-name: starRing;
    }
    .rating__input-1:checked ~ .rating__label:first-of-type .rating__star-stroke, .rating__input-2:checked ~ .rating__label:nth-of-type(-n + 2) .rating__star-stroke, .rating__input-3:checked ~ .rating__label:nth-of-type(-n + 3) .rating__star-stroke, .rating__input-4:checked ~ .rating__label:nth-of-type(-n + 4) .rating__star-stroke, .rating__input-5:checked ~ .rating__label:nth-of-type(-n + 5) .rating__star-stroke {
      animation-name: starStroke;
    }
    .rating__input-1:checked ~ .rating__label:first-of-type .rating__star-line, .rating__input-2:checked ~ .rating__label:nth-of-type(-n + 2) .rating__star-line, .rating__input-3:checked ~ .rating__label:nth-of-type(-n + 3) .rating__star-line, .rating__input-4:checked ~ .rating__label:nth-of-type(-n + 4) .rating__star-line, .rating__input-5:checked ~ .rating__label:nth-of-type(-n + 5) .rating__star-line {
      animation-name: starLine;
    }
    .rating__input-1:checked ~ .rating__label:first-of-type .rating__star-fill, .rating__input-2:checked ~ .rating__label:nth-of-type(-n + 2) .rating__star-fill, .rating__input-3:checked ~ .rating__label:nth-of-type(-n + 3) .rating__star-fill, .rating__input-4:checked ~ .rating__label:nth-of-type(-n + 4) .rating__star-fill, .rating__input-5:checked ~ .rating__label:nth-of-type(-n + 5) .rating__star-fill {
      animation-name: starFill;
    }
    .rating__input-1:not(:checked):hover ~ .rating__label:first-of-type .rating__star-fill, .rating__input-2:not(:checked):hover ~ .rating__label:nth-of-type(2) .rating__star-fill, .rating__input-3:not(:checked):hover ~ .rating__label:nth-of-type(3) .rating__star-fill, .rating__input-4:not(:checked):hover ~ .rating__label:nth-of-type(4) .rating__star-fill, .rating__input-5:not(:checked):hover ~ .rating__label:nth-of-type(5) .rating__star-fill {
      fill: var(--yellow-t);
    }
    .rating__sr {
      clip: rect(1px, 1px, 1px, 1px);
      overflow: hidden;
      position: absolute;
      width: 1px;
      height: 1px;
    }

    @media (prefers-color-scheme: dark) {
      :root {
        --bg: #17181c;
        --fg: #e3e4e8;
      }

      .rating {
        margin: auto;
      }
      .rating__star-stroke {
        stroke: #454954;
      }
    }
    @keyframes starRing {
      from, 20% {
        animation-timing-function: ease-in;
        opacity: 1;
        r: 8px;
        stroke-width: 16px;
        transform: scale(0);
      }
      35% {
        animation-timing-function: ease-out;
        opacity: 0.5;
        r: 8px;
        stroke-width: 16px;
        transform: scale(1);
      }
      50%, to {
        opacity: 0;
        r: 16px;
        stroke-width: 0;
        transform: scale(1);
      }
    }
    @keyframes starFill {
      from, 40% {
        animation-timing-function: ease-out;
        transform: scale(0);
      }
      60% {
        animation-timing-function: ease-in-out;
        transform: scale(1.2);
      }
      80% {
        transform: scale(0.9);
      }
      to {
        transform: scale(1);
      }
    }
    @keyframes starStroke {
      from {
        transform: scale(1);
      }
      20%, to {
        transform: scale(0);
      }
    }
    @keyframes starLine {
      from, 40% {
        animation-timing-function: ease-out;
        stroke-dasharray: 1 23;
        stroke-dashoffset: 1;
      }
      60%, to {
        stroke-dasharray: 12 12;
        stroke-dashoffset: -12;
      }
    }
  </style>
</head>
<body>
<form class="rating">
  <div class="rating__stars">
    <input id="rating-1" class="rating__input rating__input-1" type="radio" name="rating" value="1">
    <input id="rating-2" class="rating__input rating__input-2" type="radio" name="rating" value="2">
    <input id="rating-3" class="rating__input rating__input-3" type="radio" name="rating" value="3">
    <input id="rating-4" class="rating__input rating__input-4" type="radio" name="rating" value="4">
    <input id="rating-5" class="rating__input rating__input-5" type="radio" name="rating" value="5">
    <label class="rating__label" for="rating-1">
      <svg class="rating__star" width="32" height="32" viewBox="0 0 32 32" aria-hidden="true">
        <g transform="translate(16,16)">
          <circle class="rating__star-ring" fill="none" stroke="#000" stroke-width="16" r="8" transform="scale(0)" />
        </g>
        <g stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <g transform="translate(16,16) rotate(180)">
            <polygon class="rating__star-stroke" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="none" />
            <polygon class="rating__star-fill" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="#000" />
          </g>
          <g transform="translate(16,16)" stroke-dasharray="12 12" stroke-dashoffset="12">
            <polyline class="rating__star-line" transform="rotate(0)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(72)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(144)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(216)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(288)" points="0 4,0 16" />
          </g>
        </g>
      </svg>
      <span class="rating__sr">1 star—极差</span>
    </label>
    <label class="rating__label" for="rating-2">
      <svg class="rating__star" width="32" height="32" viewBox="0 0 32 32" aria-hidden="true">
        <g transform="translate(16,16)">
          <circle class="rating__star-ring" fill="none" stroke="#000" stroke-width="16" r="8" transform="scale(0)" />
        </g>
        <g stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <g transform="translate(16,16) rotate(180)">
            <polygon class="rating__star-stroke" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="none" />
            <polygon class="rating__star-fill" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="#000" />
          </g>
          <g transform="translate(16,16)" stroke-dasharray="12 12" stroke-dashoffset="12">
            <polyline class="rating__star-line" transform="rotate(0)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(72)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(144)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(216)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(288)" points="0 4,0 16" />
          </g>
        </g>
      </svg>
      <span class="rating__sr">2 stars—不咋好</span>
    </label>
    <label class="rating__label" for="rating-3">
      <svg class="rating__star" width="32" height="32" viewBox="0 0 32 32" aria-hidden="true">
        <g transform="translate(16,16)">
          <circle class="rating__star-ring" fill="none" stroke="#000" stroke-width="16" r="8" transform="scale(0)" />
        </g>
        <g stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <g transform="translate(16,16) rotate(180)">
            <polygon class="rating__star-stroke" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="none" />
            <polygon class="rating__star-fill" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="#000" />
          </g>
          <g transform="translate(16,16)" stroke-dasharray="12 12" stroke-dashoffset="12">
            <polyline class="rating__star-line" transform="rotate(0)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(72)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(144)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(216)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(288)" points="0 4,0 16" />
          </g>
        </g>
      </svg>
      <span class="rating__sr">3 stars—还行</span>
    </label>
    <label class="rating__label" for="rating-4">
      <svg class="rating__star" width="32" height="32" viewBox="0 0 32 32" aria-hidden="true">
        <g transform="translate(16,16)">
          <circle class="rating__star-ring" fill="none" stroke="#000" stroke-width="16" r="8" transform="scale(0)" />
        </g>
        <g stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <g transform="translate(16,16) rotate(180)">
            <polygon class="rating__star-stroke" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="none" />
            <polygon class="rating__star-fill" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="#000" />
          </g>
          <g transform="translate(16,16)" stroke-dasharray="12 12" stroke-dashoffset="12">
            <polyline class="rating__star-line" transform="rotate(0)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(72)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(144)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(216)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(288)" points="0 4,0 16" />
          </g>
        </g>
      </svg>
      <span class="rating__sr">4 stars—很好</span>
    </label>
    <label class="rating__label" for="rating-5">
      <svg class="rating__star" width="32" height="32" viewBox="0 0 32 32" aria-hidden="true">
        <g transform="translate(16,16)">
          <circle class="rating__star-ring" fill="none" stroke="#000" stroke-width="16" r="8" transform="scale(0)" />
        </g>
        <g stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <g transform="translate(16,16) rotate(180)">
            <polygon class="rating__star-stroke" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="none" />
            <polygon class="rating__star-fill" points="0,15 4.41,6.07 14.27,4.64 7.13,-2.32 8.82,-12.14 0,-7.5 -8.82,-12.14 -7.13,-2.32 -14.27,4.64 -4.41,6.07" fill="#000" />
          </g>
          <g transform="translate(16,16)" stroke-dasharray="12 12" stroke-dashoffset="12">
            <polyline class="rating__star-line" transform="rotate(0)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(72)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(144)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(216)" points="0 4,0 16" />
            <polyline class="rating__star-line" transform="rotate(288)" points="0 4,0 16" />
          </g>
        </g>
      </svg>
      <span class="rating__sr">5 stars—惊喜</span>
    </label>
    <p class="rating__display" data-rating="1" hidden>极差</p>
    <p class="rating__display" data-rating="2" hidden>不咋好</p>
    <p class="rating__display" data-rating="3" hidden>还行</p>
    <p class="rating__display" data-rating="4" hidden>很好</p>
    <p class="rating__display" data-rating="5" hidden>惊喜</p>
  </div>
</form>
</body>
<script>
  window.addEventListener("DOMContentLoaded",() => {
    const starRating = new StarRating("form");
  });

  class StarRating {
    constructor(qs) {
      this.ratings = [
        {id: 1, name: "极差"},
        {id: 2, name: "不咋好"},
        {id: 3, name: "还行"},
        {id: 4, name: "很好"},
        {id: 5, name: "惊喜"}
      ];
      this.rating = null;
      this.el = document.querySelector(qs);

      this.init();
    }
    init() {
      this.el?.addEventListener("change",this.updateRating.bind(this));

      // 阻止Firefox在刷新之间保留表单数据
      try {
        this.el?.reset();
      } catch (err) {
        console.error("Element isn’t a form.");
      }
    }
    updateRating(e) {
      // 清除动画延迟
      Array.from(this.el.querySelectorAll(`[for*="rating"]`)).forEach(el => {
        el.className = "rating__label";
      });

      const ratingObject = this.ratings.find(r => r.id === +e.target.value);
      const prevRatingID = this.rating?.id || 0;

      let delay = 0;
      this.rating = ratingObject;
      this.ratings.forEach(rating => {
        const { id } = rating;

        // 加上延迟
        const ratingLabel = this.el.querySelector(`[for="rating-${id}"]`);

        if (id > prevRatingID + 1 && id <= this.rating.id) {
          ++delay;
          ratingLabel.classList.add(`rating__label--delay${delay}`);
        }

        // 隐藏未阅读的评分,显示要阅读的评分
        const ratingTextEl = this.el.querySelector(`[data-rating="${id}"]`);

        if (this.rating.id !== id)
          ratingTextEl.setAttribute("hidden",true);
        else
          ratingTextEl.removeAttribute("hidden");
      });
    }
  }
</script>
</html>