วงกลมแสดงความคืบหน้า

วงกลมแสดงความคืบหน้า (หรือแถบความคืบหน้ารูปวงกลม) จะแสดงภาพสถานะของงาน แถบความคืบหน้าแบบวงกลมต่างจากแถบความคืบหน้าแบบเส้นตรงตรงที่มีรูปทรงกลมซึ่งเหมาะสำหรับแดชบอร์ด คอมโพเนนต์การ์ด หรือที่ใดก็ตามที่มีพื้นที่จำกัด

คู่มือนี้จะใช้แหวนแสดงความคืบหน้าโดยทำดังนี้

  • การใช้องค์ประกอบ <progress> แบบเนทีฟเป็นรากฐานเชิงความหมายเพื่อให้มั่นใจว่าคอมโพเนนต์จะเข้าถึงได้สำหรับโปรแกรมอ่านหน้าจอและผู้ใช้แป้นพิมพ์ตั้งแต่แกะกล่อง
  • การจัดสไตล์คอมโพเนนต์ด้วย conic-gradient() และ mask-image ช่วยให้วงแหวนตอบสนองได้อย่างเต็มที่และปรับธีมได้โดยไม่ต้องจัดการเส้นทาง SVG ที่ซับซ้อน
  • ใช้ประโยชน์จากพร็อพเพอร์ตี้ที่กำหนดเองของ CSS และ @property เพื่อให้การเปลี่ยนผ่านของแถบความคืบหน้าเป็นไปอย่างราบรื่นและเร่งด้วย GPU

วิธีนี้เป็นที่นิยมมากกว่าโซลูชันที่ใช้ SVG เท่านั้น เนื่องจากใช้องค์ประกอบ <progress> เชิงความหมายแทน ARIA และผสานรวมกับเลย์เอาต์ ระบบการออกแบบ และการพิมพ์ที่มีอยู่ได้ง่ายกว่า

ดูไอคอนหมุนแสดงการโหลดเพื่อจัดการสถานะการโหลดที่ไม่แน่นอน

การใช้งาน

1. Markup

ใช้ Wrapper เพื่อเก็บทั้งวงแหวนภาพและเนื้อหาตรงกลางที่ไม่บังคับ องค์ประกอบ <progress> ยังคงเป็นแหล่งข้อมูลความจริงเชิงความหมาย ใช้คลาสยูทิลิตีเพื่อซ่อนแถบความคืบหน้าแบบเนทีฟในขณะที่ยังคงเข้าถึงได้

<div class="ring-wrapper">
  <progress value="75" max="100" aria-label="Task progress" class="progress-ring"></progress>
  <!-- Optional: Content to display in the center -->
  <div class="ring-content">
    75%
  </div>
</div>

2. รูปแบบ

การซ่อน UI แบบเนทีฟ

หากต้องการจัดสไตล์องค์ประกอบ <progress> เป็นวงกลมแสดงความคืบหน้า ให้ซ่อนสไตล์แถบความคืบหน้าเริ่มต้นของเบราว์เซอร์ก่อน

/* Hide native bars */
progress.loading-spinner:indeterminate::-webkit-progress-bar {
  display: none;
  background: none;
}
progress.loading-spinner:indeterminate::-webkit-progress-value {
  display: none;
  background: none;
}
progress.loading-spinner:indeterminate::-moz-progress-bar {
  display: none;
  background: none;
}
progress.loading-spinner:indeterminate::slider-fill {
  display: none;
  background: none;
}

คอนเทนเนอร์และวงแหวน

Wrapper จะให้บริบทการวางตำแหน่ง องค์ประกอบ <progress> จะจัดการการไล่ระดับสีที่มองเห็นได้

.progress-ring-wrapper {
  position: relative;
  display: grid;
  place-items: center;
}

progress.progress-ring {
  --size: 150px;
  --thickness: 16px;
  --track-color: #f1f5f9;
  --fill-color: #3b82f6;
  --value: attr(value type(<number>));
  width: var(--size);
  height: var(--size);

  transition: --value 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  border-radius: 50%;

  background: conic-gradient(
    var(--fill-color) calc(var(--value) * 1%),
    var(--track-color) 0
  );

  /* MANDATORY: Clip the background to the border-area. */
  background-clip: border-area;
  border: var(--thickness) solid transparent;
  background-origin: border-box;
}

.progress-ring-content {
  /* Positioned in the center of the wrapper */
  position: absolute;
}

นอกจากนี้ คุณยังใช้radial-gradientเพื่อสร้างฝาปิดปลายมนได้ด้วย

เปิดใช้การเปลี่ยนผ่านที่ราบรื่นด้วย @property

หากต้องการทำให้วงกลมแสดงความคืบหน้าเคลื่อนไหวอย่างราบรื่นเมื่อค่าเปลี่ยนแปลง ให้ลงทะเบียน --value เป็นพร็อพเพอร์ตี้ที่กำหนดเองที่เป็นตัวเลข

ผู้ใช้ที่ไวต่อการเคลื่อนไหวอาจรู้สึกสับสนเมื่อมีการเปลี่ยนค่า เคารพคิวรี่สื่อ prefers-reduced-motion โดยมีระยะเวลา 0 วินาที (ทันที) โดยค่าเริ่มต้น และตั้งค่าเวลาให้นานขึ้นสำหรับผู้ใช้ที่ไม่มีค่ากำหนด

@property --value {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

progress.progress-ring {
  transition: --value 0s ease-in-out;
  @media (prefers-reduced-motion: no-preference) {
    transition-duration: 0.4s;
  }
}

3. ข้อมูลอัปเดตความคืบหน้า

อัปเดตแอตทริบิวต์ value ในองค์ประกอบ <progress> และเนื้อหาข้อความของ .progress-ring-content (หากแสดงเปอร์เซ็นต์) ทุกครั้งที่ค่ามีการเปลี่ยนแปลง

4. สถานะความสำเร็จที่ไม่บังคับ

คุณสามารถใช้ตัวเลือกแอตทริบิวต์ CSS เพื่ออัปเดตลักษณะที่ปรากฏของวงแหวนโดยอัตโนมัติ (เช่น เปลี่ยนสีเป็นสีเขียว) เมื่องานเสร็จสมบูรณ์

/* Change the fill color to green when the progress reaches 100% */
progress.progress-ring[value="100"] {
  --fill-color: #10b981;
}

กลยุทธ์สำรอง

อย่าเพิ่มค่าสำรองภายในองค์ประกอบ <progress> เทคโนโลยีความช่วยเหลือพิเศษไม่ได้ใช้แอตทริบิวต์นี้ และเบราว์เซอร์ที่ทันสมัยทั้งหมดจะละเว้นแอตทริบิวต์นี้

ภาพเคลื่อนไหวสำรอง

หากไม่รองรับ @property วงแหวนจะข้ามไปยังค่าใหม่ทันทีแทนที่จะเปลี่ยนอย่างราบรื่น ในกรณีส่วนใหญ่ การดำเนินการนี้เป็นเรื่องปกติและไม่ได้ทำให้ฟังก์ชันการทำงานใดๆ เสียหาย

หากจำเป็นต้องใช้การเปลี่ยนผ่านจริงๆ คุณสามารถตรวจสอบการรองรับ @property และใช้ลูป requestAnimationFrame() เพื่อแทรก --value ในเบราว์เซอร์รุ่นเก่าได้

สำหรับเบราว์เซอร์ที่ยังไม่รองรับ background-clip: border-area ให้เปลี่ยนกลับไปใช้ mask-image เพื่อทำให้ตรงกลางขององค์ประกอบ <progress> ว่างเปล่า

@supports not (background-clip: border-area) {
  mask-image: radial-gradient(
    transparent calc(50% - var(--thickness)),
    black calc(50% - var(--thickness) + 0.5px)
  );
  border: 0;
}

สำหรับเบราว์เซอร์ที่ไม่รองรับฟังก์ชัน CSS attr() สำหรับพร็อพเพอร์ตี้ใดๆ ให้ใช้ MutationObserver เพื่อซิงค์แอตทริบิวต์ value กับคุณสมบัติที่กำหนดเอง --value โดยอัตโนมัติ

if (!CSS.supports("width: attr(value type(<number>))")) {
  const observer = new MutationObserver(() => {
    progress.style.setProperty("--value", progress.getAttribute("value"));
  });
  observer.observe(progress, {
    attributes: true,
    attributeFilter: ["value"],
  });
}