من اخیراً گرافیک متحرک وبسایت خود را با موضوعی جدید و گروهی از شخصیتهای پیشگام بهروزرسانی کردهام و بسیاری از تکنیکهایی را که در این مجموعه به اشتراک گذاشتهام، بهکار بردهام. تعدادی از انیمیشنهای من وقتی با آنها یا در زمانهای مختلف روز ارتباط برقرار میکنند، ظاهرشان تغییر میکند.
رنگهای گرافیک بالای صفحات وبلاگ من از صبح تا شب هر روز تغییر میکنند. سپس، حالت برفی وجود دارد، که رنگهای سرد و تم زمستانی را اضافه میکند، به لطف یک لایه همپوشانی و یک حالت ترکیبی.
در حین کار روی این موضوع، شروع به این سوال کردم که آیا مقادیر رنگی نسبی CSS میتواند به من کنترل بیشتری بدهد و در عین حال فرآیند را سادهتر کند. توجه: در این آموزش، من بر روی مقادیر نسبی رنگ و فضای رنگی OKLCH برای طرحهای گرافیکی و انیمیشنها تمرکز خواهم کرد. اگر می خواهید در رنگ های نسبی غوطه ور شوید، احمد سایه یک راهنمای تعاملی عالی ایجاد کرده است. در مورد فضاهای رنگی، طیف ها و OKLCH، جف گراهام خودمان درباره آنها نوشت.
استفاده مکرر از عناصر کلیدی بود. پسزمینهها در صورت امکان مجدداً مورد استفاده قرار میگرفتند، با زومها و همپوشانیها که به ساخت صحنههای جدید از همان اثر هنری کمک میکرد. این از ضرورت زاده شد، اما همچنین تفکر در قالب سریال را به جای صحنه های فردی تشویق می کرد. مشکل بهروزرسانی دستی پالتهای رنگ بیایید مستقیم به چالش من برویم. در عناوین تون مانند این - بر اساس قسمت نمایش خرس یوگی در سال 1959 "Lullabye-Bye Bear" - و کار من به طور کلی، پالت ها به چند رنگ محدود محدود می شوند.
من سایه ها و ته رنگ هایی را از آنچه که رنگ "پایه" خود می نامم ایجاد می کنم تا بدون افزودن رنگ های بیشتر، پالت را گسترش دهم.
در Sketch، من در فضای رنگی HSL کار می کنم، بنابراین این فرآیند شامل افزایش یا کاهش مقدار روشنایی رنگ پایه من است. صادقانه بگویم، این کار سختی نیست - اما انتخاب رنگ پایه متفاوت مستلزم ایجاد مجموعه ای کاملاً جدید از سایه ها و رنگ ها است. انجام آن به صورت دستی، بارها و بارها، به سرعت دشوار می شود.
من به فضای رنگی HSL - H (رنگ)، S (اشباع) و L (روشنی) اشاره کردم، اما این تنها یکی از چندین راه برای توصیف رنگ است. RGB - R (قرمز)، G (سبز)، B (آبی) - احتمالاً آشناترین، حداقل در شکل Hex آن است. همچنین LAB - L (سبک)، A (سبز-قرمز)، B (آبی-زرد) - و مدل جدیدتر، اما اکنون به طور گسترده LCH - L (سبکی)، C (کروما)، H (رنگ) - به شکل OKLCH وجود دارد. با LCH - به ویژه OKLCH در CSS - می توانم مقدار روشنایی رنگ پایه خود را تنظیم کنم.
یا می توانم رنگ آن را تغییر دهم. رنگ LCH و اشباع HSL هر دو شدت یا غنای یک رنگ را توصیف می کنند، اما این کار را به روش های مختلف انجام می دهند. LCH طیف وسیع تری و ترکیب قابل پیش بینی تر بین رنگ ها را به من می دهد.
من همچنین میتوانم رنگ را تغییر دهم تا پالتی از رنگها ایجاد کنم که دارای مقادیر روشنایی و رنگی یکسان هستند. در هر دو HSL و LCH، طیف رنگ از قرمز شروع می شود، از سبز و آبی عبور می کند و به قرمز باز می گردد.
چرا OKLCH طرز فکر من در مورد رنگ را تغییر داد؟ پشتیبانی مرورگر از فضای رنگی OKLCH در حال حاضر گسترده است، حتی اگر ابزارهای طراحی - از جمله Sketch - به کار گرفته نشده باشند. خوشبختانه، این نباید شما را از استفاده از OKLCH باز دارد. مرورگرها با خوشحالی مقادیر Hex، HSL، LAB و RGB را برای شما به OKLCH تبدیل میکنند. شما می توانید یک ویژگی سفارشی CSS را با رنگ پایه در هر فضایی از جمله Hex تعریف کنید: /* رنگ پایه */ --بنیاد: #5accd6;
هر رنگ مشتق شده از آن به طور خودکار به OKLCH تبدیل می شود: --Foundation-light: oklch(از var(--Foundation) [...]؛ } --foundation-mid: oklch(از var(--foundation) [...]؛ } --Foundation-Dark: Oklch(از var(--Foundation) [...]; }
رنگ نسبی به عنوان یک سیستم طراحی به رنگ نسبی فکر کنید که می گوید: "این رنگ را بگیرید، آن را تغییر دهید، سپس نتیجه را به من بدهید." دو راه برای تنظیم یک رنگ وجود دارد: تغییرات مطلق و تغییرات متناسب. آنها از نظر کد مشابه به نظر می رسند، اما زمانی که شروع به تعویض رنگ های پایه کنید، رفتار بسیار متفاوتی دارند. درک این تفاوت چیزی است که می تواند استفاده از رنگ نسبی را به یک سیستم تبدیل کند. /* رنگ پایه */ --بنیاد: #5accd6;
به عنوان مثال، مقدار روشنایی رنگ پایه من 0.7837 است، در حالی که یک نسخه تیره تر دارای مقدار 0.5837 است. برای محاسبه تفاوت، مقدار پایینتر را از مقدار بالاتر کم میکنم و با استفاده از تابع calc() نتیجه را اعمال میکنم: --فنداسیون-تاریک: oklch(از var(--Foundation) calc (l - 0.20) c h);
برای رسیدن به رنگ روشن تر، به جای آن تفاوت را اضافه می کنم: -- فونداسیون-نور: oklch(از var(--Foundation) calc (l + 0.10) c h);
کروماتنظیمات از همین روند پیروی می کنند. برای کاهش شدت رنگ پایه از 0.1035 به 0.0035، یک مقدار را از مقدار دیگر کم می کنم: oklch(از var(--Foundation) l calc (c - 0.10) ساعت)؛
برای ایجاد یک پالت از رنگ ها، تفاوت بین مقدار رنگ پایه رنگ (200) و رنگ جدید (260) را محاسبه می کنم: oklch(از var(--Foundation) l c calc (h + 60));
آن محاسبات مطلق است. وقتی مقدار ثابتی را کم می کنم، عملاً می گویم: "همیشه این مقدار را کم کن." همین امر هنگام افزودن مقادیر ثابت اعمال می شود: calc (c - 0.10) calc (c + 0.10)
من محدودیت های این رویکرد را به سختی آموختم. وقتی به کم کردن مقادیر ثابت رنگی تکیه کردم، به محض اینکه پایه را تغییر دادم، رنگها به سمت خاکستری فرو میرفتند. پالتی که برای یک رنگ کار می کرد برای رنگ دیگر از هم پاشید. ضرب رفتار متفاوتی دارد. وقتی کروما را ضرب میکنم، به مرورگر میگویم: "شدت این رنگ را به نسبت کاهش دهید." رابطه بین رنگ ها دست نخورده باقی می ماند، حتی زمانی که پایه تغییر می کند: calc (c * 0.10)
قوانین حرکت من، مقیاس آن، چرخش آن
حرکت سبکی (جمع یا تفریق)، کروما مقیاس (ضرب)، چرخش رنگ (افزودن یا تفریق درجه).
من کروما را مقیاس میدهم زیرا میخواهم تغییرات شدت متناسب با رنگ پایه باشد. روابط رنگ چرخشی هستند، بنابراین ضرب رنگ معنی ندارد. سبکی ادراکی و مطلق است - ضرب کردن آن اغلب نتایج عجیب و غریبی ایجاد می کند.
از یک رنگ تا یک تم کامل رنگ نسبی به من این امکان را می دهد که یک رنگ پایه را تعریف کنم و هر رنگ دیگری را که نیاز دارم - پر شده، ضربه، توقف گرادیان، سایه - از آن تولید کنم. در آن نقطه، رنگ دیگر یک پالت نیست و به یک سیستم تبدیل می شود. تصاویر SVG تمایل دارند از همان چند رنگ در پرکردن ها، ضربه ها و گرادیان ها استفاده مجدد کنند. رنگ نسبی به شما امکان می دهد آن روابط را یک بار تعریف کنید و در همه جا دوباره از آنها استفاده کنید - دقیقاً مانند انیماتورها که از پس زمینه برای ایجاد صحنه های جدید استفاده می کنند.
رنگ پایه را یک بار تغییر دهید، و هر رنگ مشتق شده به طور خودکار، بدون محاسبه مجدد چیزی با دست، به روز می شود. خارج از گرافیک های متحرک، می توانم از همین رویکرد برای تعریف رنگ ها برای حالت های عناصر تعاملی مانند دکمه ها و پیوندها استفاده کنم. رنگ پایه ای که من در عنوان تون "Lullabye-Bye Bear" استفاده کردم، آبی فیروزه ای است. پس زمینه یک شیب شعاعی بین پایه من و یک نسخه تیره تر است.
برای ایجاد نسخه های جایگزین با حالات کاملاً متفاوت، فقط باید رنگ پایه را تغییر دهم: --بنیاد: #5accd6; --grad-end: var(--foundation); --grad-start: oklch(از var(--foundation) calc (l - 0.2357) calc (c * 0.833) h);
برای اتصال آن ویژگیهای سفارشی به گرادیان SVG من بدون تکرار مقادیر رنگ، مقادیر رمزگذاری شده توقف رنگ را با سبکهای درون خطی جایگزین کردم:
در مرحله بعد، من باید مطمئن می شدم که Toon Text من همیشه با هر رنگ پایه ای که انتخاب می کنم در تضاد باشد. چرخش 180 درجه ای رنگ، رنگ مکملی را تولید می کند که مطمئناً ظاهر می شود - اما می تواند به طرز ناراحت کننده ای ارتعاش کند: متن-لایت { fill: oklch(از var(--Foundation) l c calc (h + 180))؛ }
یک تغییر 90 درجه یک رنگ ثانویه زنده ایجاد می کند بدون اینکه کاملا مکمل باشد: متن-لایت { fill: oklch(از var(--Foundation) l c calc (h - 90))؛ }
بازآفرینی من از Quick Draw McGraw's Toon Title 1959 "El Kabong" از همان تکنیک ها اما با پالت متنوع تری استفاده می کند. برای مثال، شیب شعاعی دیگری بین رنگ پایه و سایه تیره تر وجود دارد.
ساختمان و درخت در پسزمینه به سادگی سایههای متفاوتی از یک رنگ پایه دارند. برای آن مسیرها، من به دو رنگ پر اضافی نیاز داشتم: .bg-mid { fill: oklch(از var(--Foundation) calc (l - 0.04) calc (c * 0.91) h); }
.bg-dark { fill: oklch(از var(--Foundation) calc (l - 0.12) calc (c * 0.64) h); }
هنگامی که پایه ها شروع به حرکت می کنند تا اینجا، هر چیزی که نشان دادهام ثابت بوده است. حتی زمانی که شخصی از انتخابگر رنگ برای تغییر رنگ پایه استفاده می کند، این تغییر فورا اتفاق می افتد. اما گرافیک های متحرک به ندرت ثابت می مانند - سرنخ در نام آن است. بنابراین، اگر رنگ بخشی از سیستم است، دلیلی وجود ندارد که آن را نیز متحرک نکند. برای متحرک کردن رنگ پایه، ابتدا باید آن را به کانال های OKLCH آن تقسیم کنم- سبکی، رنگ و رنگ. اما یک مرحله اضافی مهم وجود دارد: من باید آن مقادیر را به عنوان خصوصیات سفارشی تایپ شده ثبت کنم. اما این به چه معناست؟ بهطور پیشفرض، یک مرورگر نمیداند که یک مقدار ویژگی سفارشی CSS نشاندهنده رنگ، طول، عدد یا چیز دیگری است. این اغلب به این معنی است که نمیتوان آنها را به آرامی در طول انیمیشن درونیابی کرد و از یک مقدار به مقدار دیگر پرش کرد. ثبت یک ویژگی سفارشی به مرورگر می گوید که چه مقداری را نشان می دهد و چگونه باید در طول زمان رفتار کند. در این مورد، من میخواهم مرورگر کانالهای رنگی من را بهعنوان اعداد در نظر بگیرد تا بتوانند به راحتی متحرک شوند. @property --f-l { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 0.40; }
@property --f-c { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 0.11; }
@property --f-h { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 305; }
پس از ثبت نام، این ویژگی های سفارشی مانند CSS بومی عمل می کنند. مرورگر می تواند آنها را فریم به فریم درون یابی کند. سپس رنگ پایه را از آن کانال ها بازسازی می کنم: --بنیاد: oklch(var(--f-l) var(--f-c) var(--f-h));
این باعث می شود که رنگ پایه مانند هر مقدار عددی دیگر متحرک شود. در اینجا یک انیمیشن ساده "تنفس" وجود دارد که به آرامی سبکی را در طول زمان تغییر می دهد: @keyframes breathe { 0%, 100% { --f-l: 0.36; } 50% { --f-l: 0.46; } }
.toon-title { انیمیشن: تنفس 10 ثانیه سهولت در خارج بی نهایت. }
از آنجایی که هر رنگ دیگری در پرها، گرادیان ها و استروک ها از --foundation گرفته شده است، همه آنها با هم متحرک می شوند و هیچ چیزی نیاز به به روز رسانی دستی ندارد. یک رنگ متحرک، جلوه های بسیار در شروع این فرآیند، من تعجب کردم که آیا مقادیر رنگ نسبی CSS می توانند امکانات بیشتری را ارائه دهند و در عین حال پیاده سازی آنها را ساده تر کنند. من اخیراً یک پسزمینه جدید معدن طلا را به صفحه تماس وبسایت خود اضافه کردم، و اولین تکرار شامل لامپهای نفتی بود که میدرخشند و میچرخند.
من میخواستم کشف کنم که چگونه متحرک کردن رنگهای نسبی CSS میتواند با رنگ آمیزی آن با رنگهای لامپ، فضای داخلی معدن را واقعیتر کند. من می خواستم آنها بر دنیای اطراف خود تأثیر بگذارند، همانطور که نور واقعی می کند. بنابراین، به جای متحرک سازی چندین رنگ، یک سیستم نورپردازی کوچک ساختم که فقط یک رنگ را متحرک می کند.
اولین وظیفه من این بود که یک لایه همپوشانی بین پس زمینه و لامپ هایم ایجاد کنم: <مسیر شناسه = "پوشش" fill="var(--overlay-tint)" [...] style="mix-blend-mode: color" />
من از mix-blend-mode: color استفاده کردم زیرا در عین حفظ درخشندگی زیرین، از حالت ترکیبی استفاده کردم. از آنجایی که میخواهم وقتی انیمیشنها روشن میشوند، همپوشانی قابل مشاهده باشد، همپوشانی را انتخاب کردم: .svg-mine #overlay { نمایش: هیچ }
@media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] #overlay { نمایش: بلوک؛ Opacity: 0.5; } }
روکش در جای خود بود، اما هنوز به لامپ ها وصل نشده بود. من به یک منبع نور نیاز داشتم. لامپ های من ساده هستند و هر کدام حاوی یک عنصر دایره ای است که من با یک فیلتر آن را محو کردم. فیلتر یک تاری بسیار ملایم در کل دایره ایجاد می کند.
به جای متحرک سازی پوشش و لامپ ها به طور جداگانه، من یک نشانه رنگی "شعله" را متحرک می کنم و همه چیزهای دیگر را از آن استخراج می کنم. ابتدا، من سه ویژگی سفارشی تایپ شده را برای کانال های OKLCH ثبت می کنم: @property --fl-l { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 0.86; } @property --fl-c { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 0.12; } @property --fl-h { نحو: "<شماره>"; ارث می برد: true; مقدار اولیه: 95; }
من آن کانال ها را متحرک کردم و عمداً چند فریم را به سمت نارنجی فشار دادم تا سوسو به وضوح به عنوان نور آتش نشان داده شود:
@keyframes flame { 0%, 100% { --fl-l: 0.86; --fl-c: 0.12; --fl-h: 95; } 6% { --fl-l: 0.91; --fl-c: 0.10; --fl-h: 92; } 12% { --fl-l: 0.83; --fl-c: 0.14; --fl-h: 100; } 18% { --fl-l: 0.88; --fl-c: 0.11; --fl-h: 94; } 24% { --fl-l: 0.82; --fl-c: 0.16; --fl-h: 82; } 30% { --fl-l: 0.90; --fl-c: 0.12; --fl-h: 90; } 36% { --fl-l: 0.79; --fl-c: 0.17; --fl-h: 76; } 44% { --fl-l: 0.87; --fl-c: 0.12; --fl-h: 96; } 52% { --fl-l: 0.81; --fl-c: 0.15; --fl-h: 102; } 60% { --fl-l: 0.89; --fl-c: 0.11; --fl-h: 93; } 68% { --fl-l: 0.83; --fl-c: 0.16; --fl-h: 85; } 76% { --fl-l: 0.91; --fl-c: 0.10; --fl-h: 91; } 84% { --fl-l: 0.85; --fl-c: 0.14; --fl-h: 98; } 92% {--fl-l: 0.80; --fl-c: 0.17; --fl-h: 74; } }
سپس آن انیمیشن را به SVG تغییر دادم، بنابراین متغیرهای مشترک هم برای لامپ ها و هم برای پوشش من در دسترس هستند:
@media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] { انیمیشن: flame 3.6s infinite linear; انزوا: منزوی کردن;
/* ساخت یک رنگ شعله از کانال های متحرک */ --شعله: oklch(var(--fl-l) var(--fl-c) var(--fl-h));
/* رنگ لامپ برگرفته از شعله */ --لامپ هسته: oklch(از var(--شعله) calc(l + 0.05) calc(c * 0.70) h);
/* رنگ پوشش حاصل از همان شعله */ --overlay-tint: oklch(از var(--flame) calc(l + 0.06) calc(c * 0.65) calc(h - 10)); } }
در نهایت، من آن رنگهای مشتق شده را روی لامپهای درخشان و پوششی که بر روی آنها تأثیر میگذارند اعمال کردم: @media (prefers-reduced-motion: no-preference) { .svg-mine[data-animations=on] #mine-lamp-1 > حلقه، .svg-mine[data-animations=on] #mine-lamp-2 > حلقه { fill: var(--lamp-core); }
.svg-mine[data-animations=on] #overlay { نمایش: بلوک؛ fill: var(--overlay-tint); Opacity: 0.5; } }
وقتی شعله به سمت نارنجی تغییر می کند، لامپ ها گرم می شوند و صحنه با آنها گرم می شود. وقتی شعله خنک شد همه چیز با هم ته نشین می شود. بهترین بخش این است که هیچ چیز به صورت دستی نوشته نمی شود. اگر رنگ پایه را تغییر دهم یا محدوده انیمیشن شعله را تغییر دهم، کل سیستم روشنایی به طور همزمان به روز می شود. شما می توانید نتیجه نهایی را در وب سایت من مشاهده کنید. استفاده مجدد، استفاده مجدد، بازدید مجدد آن انیماتورهای Hanna-Barbera مجبور شدند از روی ناچاری عناصر را تغییر کاربری دهند، اما من از رنگها دوباره استفاده میکنم، زیرا کار من را سازگارتر و نگهداری آسانتر میکند. مقادیر رنگ نسبی CSS به من اجازه می دهد:
یک رنگ پایه را تعریف کنید، نحوه ارتباط رنگ های دیگر با آن را شرح دهید، استفاده مجدد از این روابط در همه جا، و با تغییر یک مقدار، سیستم را متحرک کنید.
رنگ نسبی فقط موضوع بندی را آسان نمی کند. این روش تفکری را تشویق می کند که در آن رنگ، مانند حرکت، عمدی است - و تغییر یک مقدار می تواند کل صحنه را بدون بازنویسی اثر زیر آن تغییر دهد.