حلقه پیشرفت

حلقه پیشرفت (یا نوار پیشرفت دایره‌ای) بازخورد دیداری درباره وضعیت تکلیف ارائه می‌دهد. برخلاف نوار پیشرفت خطی، شکل دایره‌ای آن برای داشبوردها، مؤلفه‌های کارت، یا هر جایی که فضا محدود است ایده‌آل است.

این راهنما حلقه پیشرفت را با انجام کارهای زیر پیاده‌سازی می‌کند:

  • استفاده از عنصر بومی <progress> به‌عنوان پایه معنایی برای اطمینان از اینکه مؤلفه برای صفحه‌خوان‌ها و کاربران صفحه‌کلید به‌صورت آماده قابل‌دسترس است.
  • سبک‌بندی مؤلفه با conic-gradient() و mask-image، که امکان ایجاد حلقه‌ای کاملاً واکنش‌گرا و قابل‌تغییر زمینه را بدون پیچیدگی دستکاری مسیر SVG فراهم می‌کند.
  • استفاده از «ویژگی‌های سفارشی CSS» و @property برای فعال کردن انتقال‌های روان و با شتاب GPU برای پر کردن پیشرفت.

این رویکرد نسبت‌به راه‌حل‌های فقط SVG ترجیح داده می‌شود زیرا از عنصر معنایی <progress> به‌جای ARIA استفاده می‌کند و راحت‌تر با چیدمان، سیستم‌های طراحی، و حروف‌چینی موجود ادغام می‌شود.

برای مدیریت وضعیت‌های بارگیری نامشخص، گردونه بار کردن را ببینید.

پیاده‌سازی

۱. نشانه‌گذاری

از یک پوشاننده برای نگه داشتن حلقه دیداری و محتوای اختیاری مرکز استفاده کنید. عنصر <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>

۲. سبک‌ها

پنهان کردن واسط کاربر بومی

برای سبک‌دهی به عنصر <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;
}

ظرف و حلقه

پوشش‌دهنده بافت موقعیت‌یابی را ارائه می‌دهد. عنصر <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 احترام بگذارید.

@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;
  }
}

۳. به‌روزرسانی‌های پیشرفت

هرگاه مقدار تغییر کرد، مشخصه value را در عنصر <progress> و محتوای نوشتاری .progress-ring-content (اگر درصد نمایش داده می‌شود) به‌روز کنید.

۴. وضعیت موفقیت اختیاری

می‌توانید از انتخابگر مشخصه 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"],
  });
}