وقتی از من خواسته شد که یک مزرعه لوگوی اسکرول خودکار بسازم، باید از خودم می پرسیدم: «یعنی مثل یک <marquee>؟” این عجیب ترین درخواست نیست، اما فکر یک <marquee> روزهای وب “قدیمی” را که Geocities حکومت می کرد را تداعی می کند. بعد چه چیزی بود، یک پس‌زمینه GIF تکشاخ درخشان؟

اگر وسوسه شدید که به آن دست پیدا کنید <marquee> عنصر، نکن MDN یک هشدار جدی در مورد آن درست در بالای صفحه دارد:

منسوخ: این ویژگی دیگر توصیه نمی شود. اگرچه ممکن است برخی از مرورگرها همچنان از آن پشتیبانی کنند، ممکن است قبلاً از استانداردهای وب مربوطه حذف شده باشد، ممکن است در حال حذف شدن باشد یا فقط برای اهداف سازگاری نگهداری شود. از استفاده از آن خودداری کنید و در صورت امکان کد موجود را به روز کنید […] توجه داشته باشید که این ویژگی ممکن است در هر زمانی از کار بیفتد.”

این خوب است زیرا هر ویژگی اسکرول نامحدود باشد <marquee> ارائه شده است، مطمئناً می توانیم در CSS کار کنیم. اما زمانی که نمونه هایی را برای کمک به من بررسی کردم، از یافتن موارد بسیار کمی در آن شگفت زده شدم. شاید المان های اسکرول خودکار این روزها مورد توجه نباشد. شاید ماهیت محض رفتار پیمایش خودکار به اندازه کافی یک پرچم قرمز دسترسی باشد تا ما را بترساند.

در هر صورت، ما ابزارهای لازم برای انجام این کار را داریم و می‌خواستم نحوه انجام آن را به اشتراک بگذارم. این یکی از آن چیزهایی است که می توان آن را به روش های مختلف انجام داد و از بسیاری از ویژگی های مختلف CSS استفاده کرد. حتی با وجود اینکه قصد ندارم همه آنها را به طور کامل بررسی کنم، فکر می کنم دیدن روند تفکر دیگران بسیار زیبا است، و این چیزی است که شما در این مقاله از من خواهید گرفت.

چیزی که ما می سازیم

اما ابتدا مثالی از نتیجه نهایی آورده شده است:

قلم را ببینید [CSS only marquee without HTML duplication [forked]](https://codepen.io/smashingmag/pen/YzMQMXe) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML [forked] توسط سیلوستار بیستروویچ.

ایده نسبتاً ساده است. ما نوعی ظرف می‌خواهیم، ​​و در آن، یک سری عکس می‌خواهیم که بی‌نهایت بدون پایان حرکت کنند. به عبارت دیگر، همانطور که آخرین تصویر به داخل اسلاید می‌شود، می‌خواهیم اولین تصویر در مجموعه مستقیماً آن را در یک حلقه بی‌نهایت دنبال کند.

بنابراین، این طرح است: ابتدا HTML را تنظیم می کنیم، سپس ظرف را انتخاب می کنیم و مطمئن می شویم که تصاویر به درستی در آن قرار گرفته اند قبل از اینکه به نوشتن انیمیشن CSS بپردازیم که همه آن ها را با هم جمع می کند.

نمونه های موجود

همانطور که اشاره کردم، سعی کردم چند ایده را جستجو کنم. در حالی که دقیقاً چیزی را که به دنبالش بودم پیدا نکردم، اما چند دمو پیدا کردم که جرقه ای از الهام را ارائه کردند. چیزی که من واقعاً می خواستم این بود که فقط از CSS استفاده کنم در حالی که نیازی به “کلون کردن” آیتم های خیمه ای نداشتم.

جف گراهام “جلوه پس زمینه کشویی” به چیزی که می خواستم نزدیک است. در حالی که تاریخ آن است، به من کمک کرد تا ببینم چگونه می توانم عمدا استفاده کنم overflow به تصاویر اجازه می دهد تا از ظرف خارج شوند و انیمیشنی که برای همیشه حلقه می شود. با این حال، این یک تصویر پس‌زمینه است و بر مقادیر عددی فوق‌العاده خاص تکیه دارد که استفاده مجدد آن را در پروژه‌های دیگر سخت می‌کند.

قلم را ببینید [Untitled [forked]](https://codepen.io/smashingmag/pen/LYvLvGz) توسط @css-tricks.

قلم را ببینید بدون عنوان [forked] توسط @css-tricks.

یک مثال عالی دیگر از سفر کدنویسی در CodePen:

قلم را ببینید [Marquee-like Content Scrolling [forked]](https://codepen.io/smashingmag/pen/yLrXrVY) توسط سفر کدنویسی.

قلم را ببینید پیمایش محتوا مانند خیمه شب بازی [forked] توسط سفر کدنویسی.

این اثر همان چیزی است که من مطمئناً دنبال آن هستم، اما از مقداری جاوا اسکریپت استفاده می کند، و حتی اگر فقط یک پاشیدن سبک است، ترجیح می دهم جاوا اسکریپت را از ترکیب خارج کنم.

رایان مولیگان “CSS Marquee Logo Wall” نزدیک ترین چیز است. نه تنها یک مزرعه لوگو با تصاویر جداگانه است، بلکه نشان می‌دهد که چگونه می‌توان از پوشش CSS برای پنهان کردن تصاویر هنگام لغزش به داخل و خارج ظرف استفاده کرد. من توانستم همین ایده را در کارم ادغام کنم.

قلم را ببینید [CSS Marquee Logo Wall [forked]](https://codepen.io/smashingmag/pen/ExJXJZm) توسط رایان مولیگان.

قلم را ببینید CSS Marquee Logo Wall [forked] توسط رایان مولیگان.

اما هنوز چیز دیگری وجود دارد که من دنبال آن هستم. آنچه من می خواهم کمترین مقدار HTML ممکن است، یعنی نشانه گذاری که نیازی به تکرار ندارد تا این تصور ایجاد شود که تعداد بی پایانی از تصاویر وجود دارد. به عبارت دیگر، ما باید بتوانیم یک سری تصاویر با اسکرول بی‌نهایت ایجاد کنیم که در آن تصاویر تنها عناصر فرزند در کانتینر «مارکی» هستند.

من چند نمونه دیگر را در جاهای دیگر پیدا کردم، اما اینها برای راهنمایی من کافی بود. با من همراه باشید

HTML

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

<figure class="marquee">
  <img class="marquee__item" src="https://smashingmagazine.com/2024/04/infinite-scrolling-logos-html-css/logo-1.png" width="100" height="100" alt="Company 1">
  <img class="marquee__item" src="logo-2.png" width="100" height="100" alt="Company 2">
  <img class="marquee__item" src="logo-3.png" width="100" height="100" alt="Company 3">
</figure>

این کار باعث می شود که تا حد ممکن همه چیز “مسطح” باقی بماند. نباید چیز دیگری در اینجا وجود داشته باشد تا بتوانیم کارها را انجام دهیم.

راه اندازی کانتینر

Flexbox ممکن است ساده ترین روش برای ایجاد یک ردیف از تصاویر با فاصله بین آنها باشد. حتی لازم نیست به آن بگوییم که در جهت ردیف جریان یابد زیرا این پیش فرض است.

.marquee {
  display: flex;
}

من قبلاً می‌دانم که قصد دارم از موقعیت‌یابی مطلق بر روی عناصر تصویر استفاده کنم، بنابراین منطقی است که موقعیت نسبی را روی ظرف تنظیم کنیم، حاوی آنها را و از آنجایی که تصاویر در یک موقعیت مطلق قرار دارند، هیچ گونه ارتفاع یا ابعاد اختصاصی ندارند که بر اندازه ظرف تأثیر بگذارد. بنابراین، ما باید صریح را اعلام کنیم block-size (معادل منطقی height). ما همچنین به حداکثر عرض نیاز داریم، بنابراین ما مرزی برای لغزش تصاویر در داخل و خارج از دید داریم، بنابراین از آن استفاده خواهیم کرد. max-inline-size (معادل منطقی max-width):

.marquee {
  --marquee-max-width: 90vw;

  display: flex;
  block-size: var(--marquee-item-height);
  max-inline-size: var(--marquee-max-width);
  position: relative;
}

توجه داشته باشید که من از چند متغیر CSS در آنجا استفاده می کنم: یکی که ارتفاع خیمه را بر اساس ارتفاع یکی از تصاویر تعریف می کند (--marquee-item-height) و یکی که حداکثر عرض خیمه را تعریف می کند (--marquee-max-width). اکنون می‌توانیم به حداکثر عرض خیمه‌ای یک مقدار بدهیم، اما باید به طور رسمی ثبت نام کرده و مقداری را به ارتفاع تصویر اختصاص دهیم، که این کار را در یک بیت انجام خواهیم داد. من فقط دوست دارم بدانم که قصد دارم با چه متغیرهایی کار کنم.

در مرحله بعد، ما می خواهیم تصاویر زمانی که خارج از ظرف هستند پنهان شوند. بر این اساس سرریز افقی را تنظیم می کنیم:

.marquee {
  --marquee-max-width: 90vw;

  display: flex;
  block-size: var(--marquee-item-height);
  max-inline-size: var(--marquee-max-width);
  overflow-x: hidden;
  position: relative;
}

و من واقعا مانند روشی که رایان مولیگان از ماسک CSS استفاده کرد. این تصور را ایجاد می کند که تصاویر در داخل و خارج از دید محو می شوند. بنابراین، بیایید آن را به ترکیب اضافه کنیم:

.marquee {
  display: flex;
  block-size: var(--marquee-item-height);
  max-inline-size: var(--marquee-max-width);
  overflow-x: hidden;
  position: relative;
  mask-image: linear-gradient(
    to right,
    hsl(0 0% 0% / 0),
    hsl(0 0% 0% / 1) 20%,
    hsl(0 0% 0% / 1) 80%,
    hsl(0 0% 0% / 0)
  );
  position: relative;
}

این چیزی است که ما تا کنون داریم:

قلم را ببینید [CSS only marquee without HTML duplication, example 0 [forked]](https://codepen.io/smashingmag/pen/LYvjLLG) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML، مثال 0 [forked] توسط سیلوستار بیستروویچ.

تعیین موقعیت اقلام Marquee

موقعیت یابی مطلق چیزی است که به ما امکان می دهد تصاویر را از جریان سند خارج کرده و به صورت دستی آنها را در موقعیت مکانی قرار دهیم تا بتوانیم از آنجا شروع کنیم.

.marquee__item {
  position: absolute;
}

این باعث می شود به نظر برسد که تصاویر کاملاً از بین رفته اند. اما آنها آنجا هستند – تصاویر مستقیماً روی یکدیگر قرار می گیرند.

به یاد داشته باشید که متغیر CSS برای ظرف ما، --marquee-item-height? اکنون می‌توانیم از آن برای مطابقت با ارتفاع آیتم خیمه‌ای استفاده کنیم:

.marquee__item {
  position: absolute;
  inset-inline-start: var(--marquee-item-offset);
}

برای فشار دادن تصاویر marquee به خارج از ظرف، باید a را تعریف کنیم --marquee-item-offset، اما این محاسبه بی اهمیت نیست، بنابراین در بخش بعدی نحوه انجام آن را خواهیم آموخت. ما می دانیم که چیست animation باید باشد: چیزی که پس از یک تاخیر اولیه برای مدت معینی به صورت خطی حرکت می کند، سپس بی نهایت ادامه می یابد. بیایید آن را با برخی از متغیرها به عنوان متغیرهای موقت وصل کنیم.

.marquee__item {
  position: absolute;
  inset-inline-start: var(--marquee-item-offset);
  animation: go linear var(--marquee-duration) var(--marquee-delay, 0s) infinite;
}

برای متحرک سازی بی نهایت آیتم های marquee، باید دو متغیر CSS تعریف کنیم، یکی برای مدت زمان (--marquee-duration) و یکی برای تاخیر (--marquee-delay). مدت زمان می تواند هر طولی باشد که شما بخواهید، اما تاخیر باید محاسبه شود، که در بخش بعدی متوجه خواهیم شد.

.marquee__item {
  position: absolute;
  inset-inline-start: var(--marquee-item-offset);
  animation: go linear var(--marquee-duration) var(--marquee-delay, 0s) infinite;
  transform: translateX(-50%);
}

در نهایت آیتم marquee را توسط ترجمه می کنیم -50% به صورت افقی این “هک” کوچک شرایطی را که اندازه تصویر ناهموار است کنترل می کند.

قلم را ببینید [CSS only marquee without HTML duplication, example 2 [forked]](https://codepen.io/smashingmag/pen/ExJXJMQ) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML، مثال 2 [forked] توسط سیلوستار بیستروویچ.

متحرک سازی تصاویر

برای ساخت انیمیشن به اطلاعات زیر نیاز داریم:

  • عرض لوگوها،
  • ارتفاع لوگوها،
  • تعداد اقلام و
  • مدت زمان انیمیشن.

بیایید از تنظیمات زیر برای مجموعه متغیرهای خود استفاده کنیم:

.marquee--8 {
  --marquee-item-width: 100px;
  --marquee-item-height: 100px;
  --marquee-duration: 36s;
  --marquee-items: 8;
}

توجه داشته باشید: من از اصلاح کننده BEM استفاده می کنم .marquee--8 برای تعریف انیمیشن هشت لوگو. اکنون که می دانیم می توانیم فریم های کلیدی انیمیشن را تعریف کنیم --marquee-item-width ارزش.

@keyframes go {
  to {
    inset-inline-start: calc(var(--marquee-item-width) * -1);
  }
}

انیمیشن آیتم خیمه شب بازی را از راست به چپ حرکت می دهد، و به هر یک از آنها اجازه می دهد تا از سمت راست در حالی که خارج از دید در لبه سمت چپ و خارج از محفظه خیمه ای حرکت می کنند، وارد نمای شوند.

حال باید آن را تعریف کنیم --marquee-item-offset. می‌خواهیم آیتم خیمه‌ای را تا انتها به سمت راست محفظه خیمه‌ای، برخلاف حالت پایان انیمیشن، فشار دهیم.

ممکن است فکر کنید که افست باید باشد 100% + var(--marquee-item-width)، اما این باعث می شود که آرم ها روی صفحه نمایش های کوچکتر همپوشانی داشته باشند. برای جلوگیری از آن، باید حداقل عرض همه لوگوها را با هم بدانیم. ما این کار را به روش زیر انجام می دهیم:

calc(var(--marquee-item-width) * var(--marquee-items))

اما این کافی نیست. اگر محفظه خیمه‌ای خیلی بزرگ باشد، آرم‌ها کمتر از حداکثر فضا را اشغال می‌کنند و افست در داخل ظرف قرار می‌گیرد که باعث می‌شود آرم‌ها در داخل ظرف خیمه‌ای نمایان شوند. برای جلوگیری از آن، ما از آن استفاده خواهیم کرد max() عملکرد مانند زیر:

--marquee-item-offset: max(
  calc(var(--marquee-item-width) * var(--marquee-items)),
  calc(100% + var(--marquee-item-width))
);

را max() تابع بررسی می‌کند که کدام یک از دو مقدار آرگومان‌هایش بزرگ‌تر است، عرض کلی همه نشان‌ها یا حداکثر عرض ظرف به اضافه عرض لوگوی واحد، که قبلاً تعریف کردیم. مورد دوم در نمایشگرهای بزرگتر و اولی در نمایشگرهای کوچکتر صادق خواهد بود.

قلم را ببینید [CSS only marquee without HTML duplication, example 3 [forked]](https://codepen.io/smashingmag/pen/BaEZEXN) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML، مثال 3 [forked] توسط سیلوستار بیستروویچ.

در نهایت، تاخیر پیچیده انیمیشن را تعریف می کنیم (--marquee-delay) با این فرمول:

--marquee-delay: calc(var(--marquee-duration) / var(--marquee-items) * (var(--marquee-items) - var(--marquee-item-index)) * -1);

تأخیر برابر است با مدت زمان انیمیشن تقسیم بر یک چند جمله ای درجه دوم (این چیزی است که ChatGPT حداقل به من می گوید). چند جمله ای درجه دوم قسمت زیر است که در آن تعداد آیتم ها و تعداد آیتم ها را منهای شاخص آیتم فعلی ضرب می کنیم:

var(--marquee-items) * (var(--marquee-items) - var(--marquee-item-index))

توجه داشته باشید که ما از تاخیر منفی استفاده می کنیم (* -1) برای شروع انیمیشن در “گذشته”، به اصطلاح. تنها متغیر باقیمانده برای تعریف، the است --marquee-item-index (موقعیت آیتم خیمه شب بازی فعلی):

.marquee--8 .marquee__item:nth-of-type(1) {
  --marquee-item-index: 1;
}
.marquee--8 .marquee__item:nth-of-type(2) {
  --marquee-item-index: 2;
}

/* etc. */

.marquee--8 .marquee__item:nth-of-type(8) {
  --marquee-item-index: 8;
}

یک بار دیگر آن نسخه ی نمایشی نهایی را مشاهده می کنید:

قلم را ببینید [CSS only marquee without HTML duplication [forked]](https://codepen.io/smashingmag/pen/xxerNKz) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML [forked] توسط سیلوستار بیستروویچ.

بهبودها

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

قلم را ببینید [CSS only marquee without HTML duplication, example 4 [forked]](https://codepen.io/smashingmag/pen/NWmgVWN) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML، مثال 4 [forked] توسط سیلوستار بیستروویچ.

یکی دیگر از پیشرفت‌هایی که می‌توانیم با کمی تنظیم به دست آوریم، جلوگیری از شکاف‌های بزرگ در صفحه‌نمایش‌های عریض است. برای انجام این کار، تنظیم کنید max-inline-size و اعلام کنند margin-inline: auto بر روی .marquee ظرف:

قلم را ببینید [CSS only marquee without HTML duplication, example 5 [forked]](https://codepen.io/smashingmag/pen/qBwjGBJ) توسط سیلوستار بیستروویچ.

قلم را ببینید فقط CSS بدون تکرار HTML، مثال 5 [forked] توسط سیلوستار بیستروویچ.

نتیجه

شما چی فکر میکنید؟ آیا این چیزی است که می توانید ببینید که در یک پروژه از آن استفاده می کنید؟ آیا جور دیگری با آن برخورد می کنید؟ من همیشه وقتی روی چیزی با ساختار HTML تمیز و یک راه حل CSS خالص وارد می شوم خوشحال می شوم. شما می توانید اجرای نهایی را در وب سایت Heyflow.

مطالعه بیشتر در SmashingMag

سرمقاله Smashing
(gg, yk)