Gần đây tôi đã làm mới đồ họa hoạt hình trên trang web của mình với một chủ đề mới và một nhóm nhân vật tiên phong, áp dụng nhiều kỹ thuật mà tôi đã chia sẻ trong loạt bài này vào thực tế. Một số hoạt ảnh của tôi thay đổi diện mạo khi ai đó tương tác với chúng hoặc vào các thời điểm khác nhau trong ngày.
Màu sắc trong đồ họa trên các trang blog của tôi thay đổi từ sáng đến tối hàng ngày. Sau đó, có chế độ tuyết, bổ sung các màu lạnh và chủ đề mùa đông, nhờ lớp phủ và chế độ hòa trộn.
Trong khi thực hiện việc này, tôi bắt đầu tự hỏi liệu các giá trị màu tương đối của CSS có thể giúp tôi kiểm soát nhiều hơn đồng thời đơn giản hóa quy trình hay không. Lưu ý: Trong hướng dẫn này, tôi sẽ tập trung vào các giá trị màu tương đối và không gian màu OKLCH cho đồ họa và hoạt ảnh theo chủ đề. Nếu bạn muốn tìm hiểu sâu hơn về màu sắc tương đối, Ahmad Shadeed đã tạo ra một hướng dẫn tương tác tuyệt vời. Đối với không gian màu, gam màu và OKLCH, Geoff Graham của chúng tôi đã viết về chúng.
Việc sử dụng lặp đi lặp lại các yếu tố là điều quan trọng. Hình nền được sử dụng lại bất cứ khi nào có thể, với khả năng thu phóng và lớp phủ giúp tạo cảnh mới từ cùng một tác phẩm nghệ thuật. Nó ra đời là cần thiết, nhưng nó cũng khuyến khích tư duy theo chuỗi hơn là theo từng cảnh riêng lẻ. Sự cố với việc cập nhật bảng màu thủ công Hãy đi thẳng vào thử thách của tôi. Trong các tựa phim hoạt hình như thế này — dựa trên tập phim “Lullabye-Bye Bear” năm 1959 — và tác phẩm của tôi nói chung, các bảng màu được giới hạn ở một số màu chọn lọc.
Tôi tạo các sắc thái và tông màu từ cái mà tôi gọi là màu “nền” để mở rộng bảng màu mà không cần thêm nhiều màu sắc hơn.
Trong Sketch, tôi làm việc trong không gian màu HSL, vì vậy quá trình này bao gồm việc tăng hoặc giảm giá trị độ sáng của màu nền. Thành thật mà nói, đó không phải là một nhiệm vụ khó khăn - nhưng việc chọn một màu nền khác đòi hỏi phải tạo ra một loạt sắc thái và tông màu hoàn toàn mới. Làm điều đó một cách thủ công, lặp đi lặp lại sẽ nhanh chóng trở nên tốn nhiều công sức.
Tôi đã đề cập đến không gian màu HSL - H (màu sắc), S (độ bão hòa) và L (độ sáng) - nhưng đó chỉ là một trong nhiều cách để mô tả màu sắc. RGB — R (đỏ), G (xanh lục), B (xanh lam) — có lẽ là quen thuộc nhất, ít nhất là ở dạng Hex. Ngoài ra còn có LAB - L (độ sáng), A (xanh lục-đỏ), B (xanh lam-vàng) - và mô hình LCH mới hơn nhưng hiện được hỗ trợ rộng rãi - L (độ sáng), C (sắc độ), H (sắc độ) - ở dạng OKLCH. Với LCH - cụ thể là OKLCH trong CSS - tôi có thể điều chỉnh giá trị độ sáng của màu nền.
Hoặc tôi có thể thay đổi sắc độ của nó. Sắc độ LCH và độ bão hòa HSL đều mô tả cường độ hoặc độ đậm của màu, nhưng chúng thực hiện theo những cách khác nhau. LCH mang lại cho tôi phạm vi rộng hơn và khả năng hòa trộn dễ đoán hơn giữa các màu.
Tôi cũng có thể thay đổi màu sắc để tạo ra một bảng màu có cùng giá trị độ sáng và sắc độ. Trong cả HSL và LCH, phổ màu sắc bắt đầu ở màu đỏ, chuyển qua màu xanh lục và xanh lam rồi trở về màu đỏ.
Tại sao OKLCH thay đổi cách tôi nghĩ về màu sắc Hỗ trợ trình duyệt cho không gian màu OKLCH hiện đã phổ biến, ngay cả khi các công cụ thiết kế - bao gồm cả Sketch - chưa bắt kịp. May mắn thay, điều đó không ngăn cản bạn sử dụng OKLCH. Các trình duyệt sẽ vui vẻ chuyển đổi các giá trị Hex, HSL, LAB và RGB thành OKLCH cho bạn. Bạn có thể xác định thuộc tính tùy chỉnh CSS bằng màu nền trong bất kỳ không gian nào, bao gồm cả Hex: /* Màu nền */ --nền tảng: #5accd6;
Bất kỳ màu nào có nguồn gốc từ nó sẽ được tự động chuyển đổi thành OKLCH: --foundation-light: oklch(từ var(--foundation) [...]; } --foundation-mid: oklch(from var(--foundation) [...]; } --foundation-dark: oklch(from var(--foundation) [...]; }
Màu sắc tương đối như một hệ thống thiết kế Hãy nghĩ về màu sắc tương đối như sau: “Lấy màu này, chỉnh sửa nó rồi đưa cho tôi kết quả”. Có hai cách để điều chỉnh màu: thay đổi tuyệt đối và thay đổi tỷ lệ. Chúng trông giống nhau về mã nhưng hoạt động rất khác nhau khi bạn bắt đầu hoán đổi màu nền. Hiểu được sự khác biệt đó chính là điều có thể biến việc sử dụng màu sắc tương đối thành một hệ thống. /* Màu nền */ --nền tảng: #5accd6;
Ví dụ: giá trị độ sáng của màu nền của tôi là 0,7837, trong khi phiên bản tối hơn có giá trị 0,5837. Để tính chênh lệch, tôi trừ giá trị thấp hơn khỏi giá trị cao hơn và áp dụng kết quả bằng hàm calc(): --nền-tối: oklch(từ var(--foundation) calc(l - 0,20) c h);
Để đạt được màu nhạt hơn, thay vào đó tôi thêm sự khác biệt: --nền-ánh sáng: oklch(từ var(--foundation) calc(l + 0,10) c h);
sắc độviệc điều chỉnh cũng diễn ra theo quy trình tương tự. Để giảm cường độ của màu nền từ 0,1035 xuống 0,0035, tôi trừ một giá trị khỏi giá trị kia: oklch(từ var(--foundation) l calc(c - 0,10) h);
Để tạo một bảng màu, tôi tính toán sự khác biệt giữa giá trị màu của màu nền (200) và màu mới (260): oklch(từ var(--foundation) l c calc(h + 60));
Những tính toán đó là tuyệt đối. Khi tôi trừ một số tiền cố định, tôi đang nói một cách hiệu quả: “Hãy luôn trừ số tiền này”. Điều tương tự cũng áp dụng khi thêm các giá trị cố định: canxi(c - 0,10) canxi(c + 0,10)
Tôi đã học được những hạn chế của phương pháp này một cách khó khăn. Khi tôi dựa vào việc trừ đi các giá trị sắc độ cố định, màu sắc sẽ chuyển sang màu xám ngay khi tôi thay đổi nền. Một bảng màu phù hợp với màu này lại thất bại đối với màu khác. Phép nhân hoạt động khác nhau. Khi tôi nhân sắc độ, tôi sẽ nói với trình duyệt: “Giảm cường độ của màu này theo tỷ lệ”. Mối quan hệ giữa các màu sắc vẫn được giữ nguyên ngay cả khi lớp nền thay đổi: canxi(c * 0,10)
Quy tắc di chuyển của tôi, chia tỷ lệ, xoay nó
Di chuyển độ sáng (cộng hoặc trừ), Sắc độ thang đo (nhân lên), Xoay màu sắc (cộng hoặc trừ độ).
Tôi chia tỷ lệ sắc độ vì tôi muốn thay đổi cường độ tỷ lệ thuận với màu cơ bản. Các mối quan hệ về màu sắc có tính luân chuyển nên việc nhân màu sắc lên không có ý nghĩa gì. Sự nhẹ nhàng mang tính nhận thức và tuyệt đối - việc nhân nó lên thường tạo ra những kết quả kỳ lạ.
Từ một màu đến toàn bộ chủ đề Màu tương đối cho phép tôi xác định màu nền và tạo ra mọi màu khác mà tôi cần - tô, nét, điểm chuyển màu, bóng - từ màu đó. Tại thời điểm đó, màu sắc không còn là một bảng màu nữa mà bắt đầu trở thành một hệ thống. Hình minh họa SVG có xu hướng sử dụng lại một số màu giống nhau cho các màu tô, nét và chuyển màu. Màu tương đối cho phép bạn xác định các mối quan hệ đó một lần và sử dụng lại chúng ở mọi nơi — giống như các nhà làm phim hoạt hình sử dụng lại nền để tạo cảnh mới.
Thay đổi màu nền một lần và mọi màu dẫn xuất sẽ tự động cập nhật mà không cần tính toán lại bất cứ điều gì bằng tay. Ngoài đồ họa hoạt hình, tôi có thể sử dụng cách tiếp cận tương tự này để xác định màu sắc cho trạng thái của các thành phần tương tác như nút và liên kết. Màu nền mà tôi sử dụng trong Tiêu đề phim hoạt hình “Lullabye-Bye Bear” của mình là màu xanh lục lam. Nền là một gradient xuyên tâm giữa kem nền của tôi và phiên bản tối hơn.
Để tạo các phiên bản thay thế với tâm trạng hoàn toàn khác, tôi chỉ cần thay đổi màu nền: --nền tảng: #5accd6; --grad-end: var(--foundation); --grad-start: oklch(từ var(--foundation) calc(l - 0,2357) calc(c * 0,833) h);
Để liên kết các thuộc tính tùy chỉnh đó với gradient SVG của tôi mà không trùng lặp các giá trị màu, tôi đã thay thế các giá trị màu dừng được mã hóa cứng bằng các kiểu nội tuyến:
Tiếp theo, tôi cần đảm bảo rằng Văn bản Toon của tôi luôn tương phản với bất kỳ màu nền nào tôi chọn. Xoay màu sắc 180 độ sẽ tạo ra màu bổ sung chắc chắn nổi bật — nhưng có thể rung lắc khó chịu: .text-ánh sáng { điền: oklch(từ var(--foundation) l c calc(h + 180)); }
Dịch chuyển 90° tạo ra màu thứ cấp sống động mà không bổ sung đầy đủ: .text-ánh sáng { điền: oklch(từ var(--foundation) l c calc(h - 90)); }
Tác phẩm tái tạo Tiêu đề Toon “El Kabong” năm 1959 của Quick Draw McGraw của tôi sử dụng các kỹ thuật tương tự nhưng với bảng màu đa dạng hơn. Ví dụ: có một gradient xuyên tâm khác giữa màu nền và màu tối hơn.
Tòa nhà và cái cây ở hậu cảnh chỉ đơn giản là những sắc thái khác nhau của cùng một màu nền. Đối với những đường dẫn đó, tôi cần thêm hai màu tô bổ sung: .bg-mid { điền: oklch(từ var(--foundation) calc(l - 0,04) calc(c * 0,91) h); }
.bg-tối { điền: oklch(từ var(--foundation) calc(l - 0,12) calc(c * 0,64) h); }
Khi nền móng bắt đầu chuyển động
Cho đến nay, mọi thứ tôi trình bày đều ở trạng thái tĩnh. Ngay cả khi ai đó sử dụng công cụ chọn màu để thay đổi màu nền, sự thay đổi đó vẫn diễn ra ngay lập tức. Nhưng đồ họa hoạt hình hiếm khi đứng yên – manh mối nằm ở cái tên. Vì vậy, nếu màu sắc là một phần của hệ thống thì không có lý do gì nó không thể tạo hiệu ứng động.
Để tạo hiệu ứng cho màu nền, trước tiên tôi cần chia nó thành các kênh OKLCH của nó- độ sáng, sắc độ và màu sắc. Nhưng có một bước bổ sung quan trọng: Tôi cần đăng ký các giá trị đó dưới dạng thuộc tính tùy chỉnh đã nhập. Nhưng điều đó có nghĩa là gì?
Theo mặc định, trình duyệt không biết liệu giá trị thuộc tính tùy chỉnh CSS có đại diện cho màu sắc, độ dài, số hay thứ gì khác hoàn toàn hay không. Điều đó thường có nghĩa là chúng không thể được nội suy một cách trơn tru trong quá trình hoạt ảnh và chuyển từ giá trị này sang giá trị tiếp theo.
Việc đăng ký một thuộc tính tùy chỉnh sẽ cho trình duyệt biết loại giá trị mà nó đại diện và cách nó hoạt động theo thời gian. Trong trường hợp này, tôi muốn trình duyệt xử lý các kênh màu của tôi dưới dạng số để chúng có thể hoạt động mượt mà.
@property --f-l {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 0,40;
}
@property --f-c {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 0,11;
}
@property --f-h {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 305;
}
Sau khi đăng ký, các thuộc tính tùy chỉnh này hoạt động giống như CSS gốc. Trình duyệt có thể nội suy chúng theo từng khung hình. Sau đó tôi xây dựng lại màu nền từ các kênh đó: --foundation: oklch(var(--f-l) var(--f-c) var(--f-h));
Điều này làm cho màu nền trở nên sống động, giống như bất kỳ giá trị số nào khác. Đây là một hình ảnh động đơn giản “thở” nhẹ nhàng thay đổi độ sáng theo thời gian: @keyframes thở { 0%, 100% { --f-l: 0,36; } 50% { --f-l: 0,46; } }
.toon-title { hoạt hình: thở 10s dễ dàng-vào-ra vô hạn; }
Bởi vì mọi màu tô, chuyển màu và nét khác đều bắt nguồn từ --foundation, nên tất cả chúng đều hoạt hình cùng nhau và không cần phải cập nhật thủ công. Một màu hoạt hình, nhiều hiệu ứng Khi bắt đầu quá trình này, tôi tự hỏi liệu các giá trị màu tương đối của CSS có thể mang lại nhiều khả năng hơn đồng thời làm cho việc triển khai chúng trở nên đơn giản hơn hay không. Gần đây tôi đã thêm hình nền mỏ vàng mới vào trang liên hệ trên trang web của mình và phiên bản đầu tiên bao gồm đèn dầu phát sáng và lắc lư.
Tôi muốn khám phá cách hoạt hình các màu sắc tương đối trong CSS có thể làm cho nội thất mỏ trở nên chân thực hơn bằng cách pha màu cho nó bằng màu của đèn. Tôi muốn chúng tác động đến thế giới xung quanh giống như ánh sáng thực. Vì vậy, thay vì tạo hoạt ảnh nhiều màu, tôi xây dựng một hệ thống chiếu sáng nhỏ chỉ tạo hoạt ảnh cho một màu.
Nhiệm vụ đầu tiên của tôi là đặt một lớp phủ giữa nền và đèn của tôi: <đường dẫn id="lớp phủ" fill="var(--overlay-tint)" […] style="mix-blend-mode: color" />
Tôi đã sử dụng mix-blend-mode: color vì nó làm mờ những gì bên dưới nó trong khi vẫn giữ được độ chói cơ bản. Vì tôi chỉ muốn lớp phủ hiển thị khi hoạt ảnh được bật nên tôi đã chọn tham gia lớp phủ: .svg-mine #overlay { hiển thị: không có; }
@media (thích-giảm-chuyển động: không ưu tiên) { .svg-mine[data-animations=on] #overlay { hiển thị: khối; độ mờ: 0,5; } }
Lớp phủ đã được đặt đúng chỗ nhưng chưa được kết nối với đèn. Tôi cần một nguồn ánh sáng. Đèn của tôi rất đơn giản và mỗi đèn có một phần tử hình tròn mà tôi làm mờ bằng bộ lọc. Bộ lọc tạo ra một vệt mờ rất mềm trên toàn bộ vòng tròn.
Thay vì tạo hoạt ảnh cho lớp phủ và đèn riêng biệt, tôi tạo hoạt ảnh cho một mã thông báo màu “ngọn lửa” duy nhất và rút ra mọi thứ khác từ đó. Đầu tiên, tôi đăng ký ba thuộc tính tùy chỉnh đã nhập cho các kênh OKLCH:
@property --fl-l {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 0,86;
}
@property --fl-c {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 0,12;
}
@property --fl-h {
cú pháp: "";
kế thừa: đúng;
giá trị ban đầu: 95;
}
Tôi tạo hoạt ảnh cho các kênh đó, cố tình đẩy một vài khung hình về phía màu cam để ánh sáng nhấp nháy trông rõ ràng như ánh lửa:
@keyframes ngọn lửa { 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; } }
Sau đó, tôi chuyển hoạt ảnh đó sang SVG, do đó, các biến được chia sẻ sẽ có sẵn cho cả đèn và lớp phủ của tôi:
@media (thích-giảm-chuyển động: không ưu tiên) { .svg-mine[data-animations=on] { hoạt hình: ngọn lửa 3.6s tuyến tính vô hạn; cô lập: cô lập;
/* Tạo màu ngọn lửa từ các kênh hoạt hình */ --flame: oklch(var(--fl-l) var(--fl-c) var(--fl-h));
/* Màu đèn bắt nguồn từ ngọn lửa */ --lõi đèn: oklch(từ var(--flame) calc(l + 0,05) calc(c * 0,70) h);
/* Màu lớp phủ bắt nguồn từ cùng một ngọn lửa */ --overlay-tint: oklch(từ var(--flame) calc(l + 0,06) calc(c * 0,65) calc(h - 10)); } }
Cuối cùng, tôi áp dụng những màu dẫn xuất đó cho những chiếc đèn phát sáng và lớp phủ mà chúng ảnh hưởng: @media (thích-giảm-chuyển động: không ưu tiên) { .svg-mine[data-animations=on] #mine-lamp-1 > vòng tròn, .svg-mine[data-animations=on] #mine-lamp-2 > vòng tròn { điền: var(--lõi đèn); }
.svg-mine[data-animations=on] #overlay { hiển thị: khối; điền: var(--overlay-tint); độ mờ: 0,5; } }
Khi ngọn lửa chuyển sang màu cam, đèn nóng lên và khung cảnh cũng ấm theo. Khi ngọn lửa nguội đi, mọi thứ cùng lắng xuống. Phần tốt nhất là không có gì được viết bằng tay. Nếu tôi thay đổi màu nền hoặc điều chỉnh phạm vi hoạt ảnh ngọn lửa, toàn bộ hệ thống chiếu sáng sẽ cập nhật đồng thời. Bạn có thể xem kết quả cuối cùng trên trang web của tôi. Tái sử dụng, Tái sử dụng, Xem lại Những nhà làm phim hoạt hình Hanna-Barbera đó buộc phải sử dụng lại các yếu tố không cần thiết, nhưng tôi sử dụng lại màu sắc vì nó làm cho tác phẩm của tôi nhất quán hơn và dễ bảo trì hơn. Giá trị màu tương đối CSS cho phép tôi:
Xác định một màu nền tảng duy nhất, Mô tả các màu sắc khác liên quan đến nó như thế nào, Tái sử dụng những mối quan hệ đó ở mọi nơi và Làm sinh động hệ thống bằng cách thay đổi một giá trị.
Màu sắc tương đối không chỉ làm cho việc tạo chủ đề trở nên dễ dàng hơn. Nó khuyến khích cách suy nghĩ trong đó màu sắc, giống như chuyển động, là có chủ ý — và việc thay đổi một giá trị có thể biến đổi toàn bộ khung cảnh mà không cần viết lại tác phẩm bên dưới nó.