ساخت لودینگ فقط با CSS

?How to Create a CSS-Only Loader Using One Element

15 اردیبهشت 1401
How-to-Create-a-CSS-Only-Loader-Using-One-Element

اگر وب‌سایتی دارید، داشتن یک لودر (بارگذار) به کاربران کمک می کند تا بتوانند به محض کلیک کردن روی یک پیوند (لینک) یا دکمه، بفهمند که اتفاقی در حال رخ دادن است. شما می توانید از این لودر در مکان های مختلف استفاده کنید و باید تا حد امکان ساده باشد. در این مقاله نحوه ساخت لودینگ با CSS را فراخواهیم گرفت دو نوع لودر را تنها با یک div و چند خط کد CSS می سازیم. افزون بر این آن را قابل تنظیم می کنیم تا بتوانید به راحتی تغییرات مختلفی را در آن ایجاد کنید. در زیر تصویر چیزی که می خواهیم بسازیم را خواهید دید:

یک اسپینر فقط با css و یک progress bar
یک اسپینر فقط با css و یک progress bar

چگونه یک اسپینر لودر (بارگذار چرخشی) بسازیم؟

در زیر یک نسخه نمایشی از آنچه که در حال ساخت هستیم، وجود دارد:

<div class="loader"></div>

<div class="loader" style="--b: 15px;--c: blue;width: 120px;--n: 8"></div>

<div class="loader" style="--b: 5px;--c: green;width: 80px;--n: 6;--g: 20deg"></div>

<div class="loader" style="--b: 20px;--c: #000;width: 80px;--n: 15;--g: 7deg"></div>
.loader {
    --b: 10px;  /* border thickness */
    --n: 10;    /* number of dashes*/
    --g: 10deg; /* gap between dashes*/
    --c: red;   /* the color */
 
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    padding: 1px;
    background: conic-gradient(#0000,var(--c)) content-box;
    -webkit-mask:
      repeating-conic-gradient(#0000 0deg,
         #000 1deg calc(360deg/var(--n) - var(--g) - 1deg),
         #0000     calc(360deg/var(--n) - var(--g)) calc(360deg/var(--n))),
      radial-gradient(farthest-side,#0000 calc(98% - var(--b)),#000 calc(100% - var(--b)));
            mask:
      repeating-conic-gradient(#0000 0deg,
         #000 1deg calc(360deg/var(--n) - var(--g) - 1deg),
         #0000     calc(360deg/var(--n) - var(--g)) calc(360deg/var(--n))),
      radial-gradient(farthest-side,#0000 calc(98% - var(--b)),#000 calc(100% - var(--b)));
    -webkit-mask-composite: destination-in;
            mask-composite: intersect;
    animation: load 1s infinite steps(var(--n));
  }
  @keyframes load {to{transform: rotate(1turn)}}

برای دیدن نتیجه این نشانی را ببینید.

ما 4 لودر مختلف داریم که از یک کد استفاده می کنند. تنها با تغییر چند متغیر، می‌توانیم یک لودر جدید بدون نیاز به تغییر کد CSS ایجاد کنیم.

متغیرها به صورت زیر تعریف می شوند:

  • b-- ضخامت حاشیه را مشخص می کند.
  • n-- تعداد خط تیره ها را مشخص می کند.
  • g-- شکاف بین خط تیره ها را مشخص می کند. از آن جایی که ما با یک عنصر دایره ای سروکار داریم، این مقدار نشان دهنده یک زاویه است.
  • c-- رنگ را مشخص می کند.

در زیر یک تصویر برای مشاهده متغیرهای مختلف وجود دارد:

متغیرهای CSS در لودر اسپینر
متغیرهای CSS در لودر اسپینر

بیایید به کد CSS بپردازیم. ما از شکل دیگری برای نشان دادن ساخت گام به گام لودر استفاده خواهیم کرد.

تصویر گام به گام لودر اسپینر
تصویر گام به گام لودر اسپینر

ابتدا با کد زیر یک  دایره ایجاد می کنیم:

.loader {
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
}

تا اینجا هیچ چیز پیچیده ای نداریم. به استفاده از نسبت ابعاد توجه کنید که به ما اجازه می دهد فقط یک مقدار (عرض) را برای تغییر اندازه تغییر دهیم. سپس یک رنگ گرادیان مخروطی از شفاف به رنگ تعریف شده (متغیر c--) اضافه می کنیم:

.loader {
    width:100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(#0000,var(--c));
}

در این مرحله ویژگی mask را برای پنهان کردن برخی از قسمت‌های دایره به صورت تکراری معرفی می‌کنیم. این کار به متغیرهای n-- و d-- بستگی دارد. اگر به شکل دقیق نگاه کنید، متوجه الگوی زیر خواهیم شد:

بخش دیدنی
بخش نادیدنی
بخش دیدنی
بخش نادیدنی
و ادامه دارد....

برای انجام این کار، از repeating-conic-gradient (#000 0 X, #0000 0 Y) استفاده می کنیم. از 0 تا X یک رنگ مات (قسمت قابل مشاهده) و از X تا Y یک رنگ شفاف (قسمت نامرئی) داریم.

متغیرهای خود را معرفی می کنیم:

  • ما بین هر قسمت قابل مشاهده به فاصله ای برابر با g نیاز داریم بنابراین فرمول بین X و Y به صورت X = Y - g خواهد بود.
  • ما به n قسمت قابل مشاهده نیاز داریم، بنابراین فرمول Y باید Y = 360deg/n باشد. یک دایره کامل 360 درجه است، بنابراین به سادگی آن را بر n تقسیم می کنیم

کد تا الان به صورت زیر است:

.loader {
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(#0000,var(--c));
    mask: repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--g)) , #0000 0 calc(360deg/var(--n))
}

مرحله بعدی دشوارترین مرحله است، زیرا برای به دست آوردن شکل نهایی باید mask دیگری را برای ایجاد نوعی سوراخ اعمال کنیم. برای انجام این کار، از یک radial-gradient با متغیر b خود استفاده می کنیم:

radial-gradient(farthest-side,#0000 calc(100% - var(--b)),#000 0)

یک دایره کامل از با ضخامتی برابر با b را داریم.این را به mask قبلی اضافه می کنیم:

.loader {
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(#0000,var(--c));
    mask: 
     radial-gradient(farthest-side,#0000 calc(100% - var(--b)),#000 0),
     repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--g)) , #0000 0 calc(360deg/var(--n))
}

ما دو لایه mask داریم، اما نتیجه آن چیزی نیست که می خواهیم. با اجرای برنامه بالا تصویر زیر را خواهیم دید:

شاید عجیب به نظر برسد اما منطقی است. بخش نهایی چیزی نیست جز مجموع هر قسمت قابل مشاهده از هر لایه ماسک. می توانیم این رفتار را با استفاده از mask-composite تغییر دهیم. در اینجا، بایدintersect (و destination-out) را برای ویژگی پیشوندی در نظر بگیریم. کد به صورت زیر در خواهد آمد:

.loader {
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(#0000,var(--c));
    mask: 
      radial-gradient(farthest-side,#0000 calc(100% - var(--b)),#000 0),
      repeating-conic-gradient(#000 0 calc(360deg/var(--n) - var(--g)) , #0000 0 calc(360deg/var(--n));
    -webkit-mask-composite: destination-in;
            mask-composite: intersect;
}

کار ما با شکل تمام شده است. در اینجا فقط به انیمیشن نیاز داریم. گام بعدی ایجاد یک چرخش بی نهایت برای اسپینر است. تنها چیزی که باید به آن توجه کرد این است که از انیمیشن steps برای ایجاد توهم خط تیره ثابت و رنگ های متحرک استفاده کرده ایم. در اینجا یک تصویر برای مشاهده تفاوت ها وجود دارد:

انیمیشن خطی در مقابل انیمیشن steps
انیمیشن خطی در مقابل انیمیشن steps

اولی یک چرخش خطی و پیوسته شکل است (نه آن چیزی که ما می خواهیم) و دومی یک انیمیشن از هم گسسته است. در زیر کد کامل انیمیشن آمده است:

<div class="loader"></div>

<div class="loader" style="--b: 15px;--c: blue;width: 120px;--n: 8"></div>

<div class="loader" style="--b: 5px;--c: green;width: 80px;--n: 6;--g: 20deg"></div>

<div class="loader" style="--b: 20px;--c: #000;width: 80px;--n: 15;--g: 7deg"></div>
.loader {
    --b: 10px;  /* border thickness */
    --n: 10;    /* number of dashes*/
    --g: 10deg; /* gap between dashes*/
    --c: red;   /* the color */
 
    width: 100px; /* size */
    aspect-ratio: 1;
    border-radius: 50%;
    padding: 1px;
    background: conic-gradient(#0000,var(--c)) content-box;
    -webkit-mask:
      repeating-conic-gradient(#0000 0deg,
         #000 1deg calc(360deg/var(--n) - var(--g) - 1deg),
         #0000     calc(360deg/var(--n) - var(--g)) calc(360deg/var(--n))),
      radial-gradient(farthest-side,#0000 calc(98% - var(--b)),#000 calc(100% - var(--b)));
            mask:
      repeating-conic-gradient(#0000 0deg,
         #000 1deg calc(360deg/var(--n) - var(--g) - 1deg),
         #0000     calc(360deg/var(--n) - var(--g)) calc(360deg/var(--n))),
      radial-gradient(farthest-side,#0000 calc(98% - var(--b)),#000 calc(100% - var(--b)));
    -webkit-mask-composite: destination-in;
            mask-composite: intersect;
    animation: load 1s infinite steps(var(--n));
  }
  @keyframes load {to{transform: rotate(1turn)}}

در بالا چند تفاوت با کدی که در پیش آمده بود خواهید دید:

  • padding را اضافه کرده ایم: 1px و background را روی content-box قرار می دهیم
  • بین رنگ های repeating-conic-gradient به مقدار+/1deg درجه تفاوت وجود دارد.
  • چند درصد تفاوت بین رنگ داخلی radial-gradient وجود دارد.

این ها برخی ویرایش ها برای جلوگیری از اشکال های دیداری هستند. گرادیان‌ها در برخی موارد نتایج عجیب تولید می‌کنند، بنابراین برای دوری جستن از آن ها باید برخی از مقادیر را به صورت دستی تنظیم کنیم.

چگونه یک Progress Bar ایجاد کنیم؟

مانند لودر پیشین، بیایید با یک نمای کلی شروع کنیم:

برای دیدن نتیجه به این نشانی مراجعه کنید.

همان پیکربندی لودر قبلی را داریم. متغیرهای CSS که لودر را کنترل می کنند به صورت زیر هستند:

  • n-- تعداد خط تیره راه را مشخص می کند.
  • s-- عرض هر نوار را مشخص می کند.
  • g-- شکاف بین خط تیره ها را مشخص می کند.

از شکل بالا می بینیم که عرض عنصر به 3 متغیر بستگی دارد. CSS آن به صورت زیر خواهد بود:

.loader {
    width: calc(var(--n)*(var(--s) + var(--g)) - var(--g));
    height: 30px; /* use any value you want here */
    padding: var(--g);
    border: 1px solid;
}

برای تنظیم شکاف در هر طرف از padding استفاده می کنیم. سپس width (عرض) برابر با تعداد نوارها ضرب در عرض و فاصله آن ها خواهد بود. یک شکاف را حذف می کنیم زیرا برای N نوار N-1 شکاف داریم. برای ایجاد نوارها از گرادیان زیر استفاده می کنیم.

repeating-linear-gradient(90deg,
  currentColor 0 var(--s),
  #0000        0 calc(var(--s) + var(--g))
)

از 0 تا s رنگ تعریف شده و از s تا s + g یک رنگ شفاف (شکاف) است. از currentColor استفاده می کنیم که مقدار ویژگی color است. توجه داشته باشید که هیچ رنگی را در داخل border تعریف نکرده ایم بنابراین از مقدار color نیز استفاده خواهد کرد. اگر بخواهیم رنگ لودر را تغییر دهیم، فقط باید ویژگی color را تنظیم کنیم.کد تا اینجا:

.loader {
    width: calc(var(--n)*(var(--s) + var(--g)) - var(--g));
    height: 30px;
    padding: var(--g);
    border: 1px solid;
    background:
      repeating-linear-gradient(90deg,
        currentColor  0 var(--s),
        #0000 0 calc(var(--s) + var(--g))
      ) left / 100% 100% content-box no-repeat;
}

از content-box استفاده می‌کنیم تا مطمئن شویم که گرادیان، ناحیه padding را پوشش نمی‌دهد. سپس اندازه معادل100% 100% و یک موقعیت سمت چپ یا left تعریف را می کنیم. نوبت به انیمیشن رسیده است. برای این لودر، اندازه پس‌زمینه را از 0% 100% به 100% 100% تغییر می دهیم که به معنی width گرادیان از 0% تا 100% است. مانند لودر قبلی، برای داشتن یک انیمیشن مجزا به جای یک انیمیشن پیوسته، از step استفاده می کنیم.

انیمیشن خطی در مقابل انیمیشن steps
انیمیشن خطی در مقابل انیمیشن steps

می توان این کار را با افزودن کد زیر انجام داد:

.loader {
    animation: load 1.5s steps(var(--n)) infinite;
  }
  @keyframes load {
    0% {background-size: 0% 100%}
}

اگر به شکل آخر دقت کنید متوجه می شوید که انیمیشن کامل نیست. حتی اگر از N استفاده کرده باشیم، یک نوار در پایان از دست داده ایم. برای غلبه بر این، باید یک مرحله اضافی اضافه کنیم. اندازه پس‌زمینه گرادیان خود را افزایش می‌دهیم تا حاوی نوارهای N+1 باشد و از steps(N+1) استفاده می‌کنیم. این کار ما را به کد نهایی می رساند:

.loader {
    width: calc(var(--n)*(var(--s) + var(--g)) - var(--g));
    height: 30px;
    padding: var(--g);
    margin: 5px auto;
    border: 1px solid;
    background:
      repeating-linear-gradient(90deg,
        currentColor  0 var(--s),
        #0000 0 calc(var(--s) + var(--g))
      ) left / calc((var(--n) + 1)*(var(--s) + var(--g))) 100% 
      content-box no-repeat;
    animation: load 1.5s steps(calc(var(--n) + 1)) infinite;
  }
  @keyframes load {
    0% {background-size: 0% 100%}
}

توجه داشته باشید که عرض گرادیان برابر است با N+1 ضرب در عرض یک نوار و یک شکاف (به جای 100٪)

امیدواریم آموزش ساخت لودینگ با CSS مورد استفاده شما قرار گرفته باشد.


منبع: وب سایت freecodecamp

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.