مقالات

تغییر رنگ در CSS • جاش دبلیو کومئو


مقدمه

فرض کنید این اثر ذره ای را می سازیم:

این افکت چیزی است که ما در حال ساخت انیمیشن های جدیدم هستیم دوره(در برگه جدید باز می شود)و جزئیات کوچک زیادی وجود دارد، اما در این پست وبلاگ می خواهم روی یک جنبه شگفت آور جالب بزرگنمایی کنم: تغییر رنگ.

هر ذره با رنگی تصادفی شروع می شود و با محو شدن تغییر رنگ می دهد. این به نظر می رسد بسیار ساده است، اما ظاهر می تواند فریبنده باشد. من در واقع یک محدودیت جدید CSS را کشف کردم که در مورد آن نمی دانستم و به چند راه حل مختلف رسیدم.

در این آموزش، چیزهایی را که یاد گرفته‌ام به اشتراک می‌گذارم و خواهید دید که چگونه می‌توانید افکت‌های تغییر رنگ فوق‌العاده جالب در CSS ایجاد کنید!

بنابراین، در اثر ذره‌ای من، می‌خواهم هر ذره رنگ‌های پویا خود را داشته باشد.

بیایید با ساده ترین گزینه ممکن، تولید یک رنگ تصادفی با استفاده از مقادیر RGB شروع کنیم:

// Do this for every particle:
const red = Math.round(Math.random() * 255);
const green = Math.round(Math.random() * 255);
const blue = Math.round(Math.random() * 255);

particle.style.backgroundColor =
  `rgb(${red} ${green} ${blue})`;

فرمت رنگی RGB از 16.7 میلیون رنگ ممکن (256) پشتیبانی می کند3، و این کد یکی از آنها را کاملاً تصادفی انتخاب می کند. در اینجا به نظر می رسد:

رنگ های نمونه

(کلیک کنیدلمس کنید قلب برای تولید ذرات چند رنگ اول در اینجا نشان داده خواهد شد.)

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

اصلاً مشخص نیست که چگونه این کار را انجام دهیم rgb()، اما اگر به hsl()، همه چیز بسیار آسان تر می شود. ما می توانیم کاری شبیه به این انجام دهیم:

const randomHue = Math.round(
  Math.random() * 359
);
particle.style.backgroundColor =
  `hsl(${randomHue}deg 100% 80%)`;

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

رنگ های نمونه

(کلیک کنیدلمس کنید قلب برای تولید ذرات چند رنگ اول در اینجا نشان داده خواهد شد.)

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

خوب، من از محلول رنگ جامد خود راضی هستم، اما واقعاً هدف ما حرکت دادن هر ذره است بین دو رنگ متفاوت

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

const fromHue = Math.round(
  Math.random() * 359
);
const toHue = fromHue + 180;

particle.style.setProperty(
  '--from-color',
  `hsl(${fromHue}deg 100% 80%)`
);
particle.style.setProperty(
  '--to-color',
  `hsl(${toHue}deg 100% 80%)`
);

/* And then, in the CSS: */
@keyframes colorShift {
  from {
    background: var(--from-color);
  }
}

.particle {
  background-color: var(--to-color);
  animation: colorShift 1500ms linear;
}

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

من یک ویژگی همگام سازی خطی را انتخاب کردم زیرا در این مورد خاص طبیعی تر به نظر می رسد. هر رنگ میانی برای مدت زمان مشابهی نمایش داده می شود.

در اینجا به نظر می رسد:

رنگ های نمونه

(کلیک کنیدلمس کنید قلب برای تولید ذرات چند رنگ اول در اینجا نشان داده خواهد شد.)

نظر شما در مورد آن چیست؟ کمی وقت بگذارید تا واقعاً به آن نگاه کنید. آیا رنگ ها به شما خوب می آیند؟

من شخصاً رنگ ها را کمی شسته به نظر می رسیدم. ذرات بین مبدا و مقصد تا حدودی خاکستری می شوند. 🤔

اگر بزرگ‌نمایی کنیم و بزرگ‌نمایی کنیم، واضح‌تر می‌شود:

اگر این ویدیو را در حالی که ذرات در نیمه جابجایی هستند متوقف کنید، همه آنها سیاه و سفید هستند!

البته، گفتن با همه چیز سخت است. در اینجا یک دکمه اساسی وجود دارد که همان انتقال رنگ را در حالت نگه داشتن/فوکوس انجام می دهد:

کد زمین بازی

style>
  button {
    background: hsl(0deg 100% 65%);
    transition: all 2000ms;

    &:hover, &:focus-visible {
      background:hsl(180deg 100% 65%);
    }
  }
style>

button>
  Hover over me
button>

رنگ شروع (قرمز) و رنگ پایانی (آبی-سبز) کاملاً زنده هستند، اما در وسط مات و خاکستری می شوند:

من می خواهم من انتظار دارم که اشباع و روشنایی یکسان می مانند و فقط رنگ در انیمیشن جابجا می شود، چیزی شبیه به این:

مشکل اینجاست: وقتی متحرک می شویم background-color، مرورگر همیشه محاسبات را با استفاده از آن انجام می دهد RGB فضای رنگی حتی زمانی که رنگ های ما در HSL مشخص شده باشند.

در RGB ما سه کانال رنگی (قرمز/سبز/آبی) داریم که هر کدام بین 0 تا 255 است. مرورگر محاسبات را روی این سه کانال انجام می دهد. به صورت جداگانه. در این دمو می توانید ببینید که چرا این مشکل وجود دارد:

نحوه استفاده: برای تغییر مکان نشانگر کلیک کنید و بکشید، یا روی نشانگر تمرکز کنید و از کلیدهای جهت دار چپ/راست استفاده کنید.

همانطور که از چپ به راست پیمایش می کنیم، کانال قرمز رنگ است کاهش می یابد مانند کانال های سبز/آبی افزایش دهد. در نتیجه، هر سه کانال به یک مقدار در وسط جمع می‌شوند و خاکستری میانی تولید می‌کنند.

به عبارت دیگر، مرورگر با استفاده از یک مدل ریاضی بین این دو رنگ سوئیچ می کند که در واقع هیچ ربطی به درک ما از رنگ ندارد. هیچ آدم عاقلی نمی گوید حد وسط قرمز و آبی خاکستری است!

مشکل دیگری وجود دارد. فرض کنید می خواهیم رنگ به طور کامل 360 درجه بچرخد و دور چرخ رنگ بچرخد. ببینید وقتی سعی می کنیم بین 0 تا 360 درجه برویم چه اتفاقی می افتد:

کد زمین بازی

style>
  button {
    background: hsl(0deg 100% 65%);
    transition: background 2000ms;

    &:hover, &:focus-visible {
      font-weight: bold;
      background: hsl(360deg 100% 65%);
    }
  }
style>

button>
  Hover over me
button>

هیچ اتفاقی نمی افتد. شما این کار را اشتباه انجام نمی دهید. به نظر می رسد نگه داشتن یا فوکوس روی دکمه به هیچ وجه روی رنگ پس زمینه تاثیری ندارد. 🤔

برای اینکه بفهمیم اینجا چه خبر است، باید به یک پاسخ دهیم فلسفی سوال: هستند hsl(0deg 100% 65%) و hsl(360deg 100% 65%) همان رنگ؟

خوب، آنها نمایندگی کند چیزی متفاوت؛ 0deg نشان دهنده اولین رنگ موجود در طیف است، در حالی که 360deg نشان دهنده یک چرخش کامل به دور چرخ رنگ است. اما در نهایت هر دو رنگ من اجازه می دهم به همان مقادیر R/G/B.

متأسفانه، همانطور که در بالا دیدیم، مرورگر درون یابی رنگ را در HSL انجام نمی دهد. ابتدا هر دو مقدار را به rgb() و سپس مقادیر هر سه کانال رنگی را منتقل می کند. و از آن زمان hsl(0deg 100% 65%) و hsl(360deg 100% 65%) هر دو به یک شکل حل می شوند rgb() ارزش، ما در نهایت هیچ انتقالی نداریم.

خوشبختانه راه حلی پیدا کردم که این مشکلات را حل می کند و عملکرد بهتری را برای بوت ارائه می دهد. ⚡

اگر آشنا نیستید، CSS filter فیلترهای سبک SVG را در CSS ارائه می دهد. به عنوان مثال، ما می توانیم یک عنصر را با آن تار کنیم filter: blur():

کد زمین بازی

style>
  img {
    
    filter: blur(6px);
  }
style>

img
  alt="A blurry 3D illustration of Josh"
  src="/img/josh-mascot.png"
/>

یکی از فیلترها است hue-rotate. همانطور که انتظار دارید، این فیلتر رنگ یک عنصر را تغییر می دهد:

عکس پرنده رنگارنگ

بیایید جلوه ذرات خود را برای استفاده به روز کنیم hue-rotate() فیلتر در سمت جاوا اسکریپت، با تنظیم رنگ پس‌زمینه ذره، می‌توانیم به آنچه قبلاً داشتیم برگردیم:

const randomHue = Math.round(
  Math.random() * 359
);

particle.style.backgroundColor =
  `hsl(${randomHue}deg 100% 80%)`;

در سمت CSS، یک انیمیشن فریم کلیدی جدید ایجاد خواهیم کرد که دو چرخش کامل در اطراف چرخ رنگ انجام می دهد:

@keyframes hueRotate {
  to {
    filter: hue-rotate(720deg);
  }
}

.particle {
  animation: hueRotate 1000ms;
}

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

رنگ های نمونه

(کلیک کنیدلمس کنید قلب برای تولید ذرات چند رنگ اول در اینجا نشان داده خواهد شد.)

کار می کند! هر ذره می تواند بیش از 180 درجه بچرخد. 😄

الان اینطور نیست خیلی زیاد همانند تغییر تفاوت ظریف جزء یک hsl() رنگ در اینجا یک مقایسه جانبی وجود دارد:

رنگ پس زمینه:

hsl(0درجه 100٪ 80٪

من کاملاً مطمئن نیستم که چگونه filter: hue-rotate() الگوریتم کار می کند، اما تمایل به ایجاد تغییرات رنگ تیره تر دارد. دریافته‌ام که اگر رنگ‌های کمی روشن‌تر و کمتر اشباع‌شده را انتخاب کنم، بهترین کار را دارد.

و راستش، من فکر می کنم دو چرخش کامل (720deg) احتمالا کوچک است. می‌خواستم مطمئن شوم که اثر در این نسخه نمایشی واضح است، اما در عمل احتمالاً مقدار کمتری را انتخاب می‌کنم، شاید چیزی در محدوده 180-540 درجه.

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

من با استفاده از ترفند جزئی فریم کلیدی که چند هفته پیش به اشتراک گذاشتم، به این هدف دست یافتم. اصل مطلب اینجاست:

@keyframes fadeToTransparent {
  to {
    opacity: 0;
  }
}

@keyframes twinkle {
  from {
    opacity: var(--twinkle-amount);
  }
  to {
    opacity: 1;
  }
}

.particle {
  animation:
    twinkle var(--twinkle-duration) infinite alternate ease-in-out,
    fadeToTransparent var(--fade-duration) 500ms;
}

یکی از رازهای کوچک انیمیشن من اضافه کردن ذرات کوچک تنوع تصادفی به همه چیز است. هر ذره ذره خود را تعریف می کند --twinkle-duration و --twinkle-amount، بنابراین همه آنها مانند چراغ های درخت کریسمس با هم پلک نمی زنند.

اگر می خواهید یاد بگیرید که چگونه این اثر ذرات کامل را به همراه تن از دیگر انیمیشن ها و تعاملات عالی، من یک خبر عالی برای شما دارم!

من 18 ماه گذشته را صرف ساختن یک دوره آنلاین تعاملی جامع در مورد انیمیشن های عجیب و غریب کرده ام. من تمام ترفندها و تکنیک های قدرتمند خود را به اشتراک می گذارم. اگر تا به حال از خود پرسیده اید که چگونه جلوه خاصی در این وبلاگ ایجاد کردم، شانس بسیار خوبی وجود دارد که این دوره پاسخ دهد. 😄

انیمیشن های عجیب و غریب

شما می توانید همه چیز را در اینجا یاد بگیرید:

آخرین به روز رسانی از

5 مه 2026


Source link

نوشته های مشابه

دیدگاهتان را بنویسید

دکمه بازگشت به بالا