ภาพหมุนแสดงการโหลด (หรือสัญญาณบอกสถานะกิจกรรม) จะแจ้งให้ผู้ใช้ทราบว่าระบบกำลังดำเนินการอยู่เมื่อไม่ทราบระยะเวลาที่แน่นอน ตัวหมุนจะ "ไม่แน่นอน" และมักใช้ภาพเคลื่อนไหวเพื่อส่งสัญญาณกิจกรรม ซึ่งต่างจากวงกลมแสดงความคืบหน้า
คู่มือนี้จะใช้ภาพหมุนโดยทำดังนี้
- การใช้องค์ประกอบ
<progress>แบบเนทีฟเป็นรากฐานเชิงความหมาย การละเว้นแอตทริบิวต์valueจะทำให้เบราว์เซอร์ถือว่าแอตทริบิวต์นี้เป็นแถบความคืบหน้าที่ไม่แน่นอน ซึ่งช่วยให้เทคโนโลยีความช่วยเหลือพิเศษประกาศได้อย่างถูกต้อง - จัดรูปแบบคอมโพเนนต์ด้วย
conic-gradient()เพื่อสร้าง "เส้นทาง" ภาพ และbackground-clip: border-areaเพื่อเจาะตรงกลางให้เป็นวงแหวน - การสร้างภาพเคลื่อนไหวของตัวหมุนอย่างมีประสิทธิภาพโดยใช้การเปลี่ยนรูปแบบ CSS และคำนึงถึง
prefers-reduced-motionเพื่อให้ผู้ใช้ทุกคนได้รับประสบการณ์การใช้งานที่สะดวกสบาย
ดูวงกลมแสดงความคืบหน้าสำหรับการจัดการงานที่กำหนดได้ซึ่งมีระยะเวลาที่ทราบ
การใช้งาน
1. Markup
ใช้องค์ประกอบ <progress> แบบเนทีฟเป็นทั้งแหล่งข้อมูลที่เชื่อถือได้เชิงความหมายและคอมโพเนนต์ภาพ หากไม่มีแอตทริบิวต์ value จะถือว่าไม่แน่นอนโดยนัย
<progress aria-label="Loading" class="loading-spinner"></progress>
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;
}
วงแหวนและเส้นทางของสปินเนอร์
สปินเนอร์ใช้ conic-gradient เพื่อสร้างเส้นทางที่มองเห็นได้
@property --arc-start {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@property --arc-end {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
/* Use a custom property to conditionally include the dash animation */
@property --progress-dash-animation {
syntax: "*";
inherits: false;
initial-value: , progress-dash 3s ease-in-out infinite;
}
progress.loading-spinner:indeterminate {
--_from: calc(90deg + var(--arc-start, 0deg));
--_to: calc(90deg + var(--arc-end, 158deg));
--size: 40px;
--thickness: 2px;
--spinner-color: #3b82f6;
--track-color: #e2e5e7;
--spinner-duration: 1.5s;
--_used-spinner-duration: var(--spinner-duration);
--spinner-timing: linear;
position: relative;
width: var(--size);
height: var(--size);
border-radius: 50%;
appearance: none;
/* Create the fading trail with dynamic angles */
background: conic-gradient(
from var(--_from),
var(--spinner-color) calc(var(--_to) - var(--_from)),
transparent 0
)
var(--track-color);
@supports (background-clip: border-area) {
background-clip: border-area;
border: var(--thickness) solid transparent;
background-origin: border-box;
}
/* ... fallback for background-clip: border-area ... */
/* The dash animation is only included if @property is supported */
animation:
progress-spin var(--_used-spinner-duration) linear infinite
var(--progress-dash-animation, );
}
@keyframes progress-spin {
to {
rotate: 1turn;
}
}
@keyframes progress-dash {
from {
--arc-start: 0deg;
--arc-end: 3deg;
}
50% {
--arc-start: 100deg;
--arc-end: 358deg;
}
to {
--arc-start: 360deg;
--arc-end: 363deg;
}
}
การเคารพค่ากำหนดการเคลื่อนไหว
ผู้ใช้ที่ไวต่อการเคลื่อนไหวอาจรู้สึกสับสนเมื่อเห็นองค์ประกอบที่หมุนเร็ว เคารพการสืบค้นสื่อ prefers-reduced-motion เสมอ ตั้งค่าพร็อพเพอร์ตี้ --_used-spinner-duration ภายในเพื่อลบล้างค่า --spinner-duration ของผู้ใช้
@media (prefers-reduced-motion: reduce) {
.loading-spinner {
/* Slow down the animation significantly rather than stopping it entirely,
so the user still knows that the process is active. */
--_used-spinner-duration: 6s;
}
}
กลยุทธ์สำรอง
หากรองรับ @property ระบบจะรวมภาพเคลื่อนไหวแบบขีดกลางโดยอัตโนมัติผ่าน initial-value ของพร็อพเพอร์ตี้ --progress-dash-animation ในเบราว์เซอร์ที่ไม่มีการรองรับ @property ระบบจะไม่สนใจการลงทะเบียนพร็อพเพอร์ตี้ และภาพเคลื่อนไหวจะเปลี่ยนกลับไปเป็นการหมุนแบบง่ายๆ ไม่จำเป็นต้องใช้ JavaScript สำหรับโฆษณาสำรองนี้
สำหรับเบราว์เซอร์ที่ยังไม่รองรับ background-clip: border-area ให้เปลี่ยนกลับไปใช้ mask-image เพื่อทำให้ตรงกลางกลวง
/* Fallback: use mask-image to create the ring */
@supports not (background-clip: border-area) {
--clip-boundary: calc(100% - var(--thickness));
mask-image: radial-gradient(
farthest-side,
transparent var(--clip-boundary),
black var(--clip-boundary)
);
border: 0;
}