Коли ти хочеш стилізувати сторінку під головне зображення — наприклад, обрати кольори для фону, кнопок або заголовків — зручно взяти палітру напряму з картинки. У попередній статті ми зробили це на чистому PHP. Сьогодні ж — розглянемо простіший і точніший спосіб: через бібліотеку ColorThief.

Що таке ColorThief?

Це PHP-порт популярної JavaScript-бібліотеки, яка дозволяє витягнути:

  • основний колір зображення;

  • палітру з кількох найчастіших кольорів.

І все це — буквально в кілька рядків.

Початковий код

Як і раніше, візьмемо невеличку папку з прикладами, просто щоб побачити, як палітри працюють з різними картинками. Але памʼятай: у реальному проєкті ти, швидше за все, оброблятимеш одне головне зображення.

Спершу встановимо бібліотеку ColorThief через Composer:

composer require ksubileau/color-thief-php

Далі підключимо бібліотеку  потрібному місці:

require_once ROOT . '/vendor/autoload.php'; // тут обов'язково перевір правильний шлях до файлу autoload.php із папки vendor – куди ти там звантажив/ла свій ColorThief
use ColorThief\ColorThief;

Файли зображень підключаємо так само, як у попередній статті:

$img_dir = __DIR__ . '/images'; // впиши тут шлях до своєї папки з зображеннями
$img_url_base = SITE_URL . '/my-awesome-slug/images'; // а тут має бути URL, по якому папка з зображеннями видима в браузері

$files = glob($img_dir . '/*.jpg'); // або .webp/.png

І так само проходимося по циклу із знайденими картинками:

foreach ($files as $img_path) {
	$filename = basename($img_path);
	$img_url = $img_url_base . '/' . $filename;

	$key_color = ColorThief::getColor($img_path, '10', null, 'hex'); // новий метод витягування ключового кольору за допомогою бібліотеки ColorThief
	draw_image_block($img_url, $key_color);
	echo '<br>';

	$palette = ColorThief::getPalette($img_path, 7, '10', null, 'hex'); // а тут за допомогою того ж ColorThief витягуємо палітру

	// Сортуємо по яскравості – нижче поясню, навіщо
	usort($palette, function($a, $b) {
		return getLuminance($a) <=> getLuminance($b);
	});

	// Виводимо палітру
	echo '<div style="width: 100%; display: flex; gap: 20px; justify-content: center; align-items: center;">';
	foreach ($palette as $color) {
		echo '<div style="background-color:' . $color . '; width:200px;height:80px;display:flex;justify-content: center;align-items: center; color: #fff;">' . $color . '</div>';
	}
	echo '</div><br>';

	// Тут буде бонус – читай до кінця 😉
}

Як це працює

Використання ColorThief зводиться до написання буквально кількох рядків — без зайвих складнощів.

$key_color = ColorThief::getColor($img_path, '10', null, 'hex');

Пояснення параметрів:

  1. Шлях до зображення

  2. Якість пошуку — чим менше число, тим точніше, але повільніше (я використовую 10, і це, на мій погляд, дає цілком добрі результати).

  3. Ділянка для аналізу — оскільки аналізуємо все зображення, вказуємо null.

  4. Формат повернення — вказуємо 'hex', інакше повернеться масив RGB (або буде помилка).

Те саме стосується палітри:

$palette = ColorThief::getPalette($img_path, 7, '10', null, 'hex');

Другий параметр тут — кількість кольорів, які потрібно витягнути. Я обрав 7 — цього достатньо для оформлення сторінки.

Виведення зображення

Для демонстрації зображення і ключового кольору використовуємо функцію:

function draw_image_block($img_url, $key_color) {
	echo '<div style="background-color:' . $key_color . '; 100%; text-align: center;">';
	echo '<img src="' . $img_url . '" style="width:60%;max-width:900px;height:auto; mask-image: linear-gradient(to right, transparent 0%, black 10%, black 90%, transparent 100%); mask-repeat: no-repeat;" />';
	echo '</div>';
}

Додатковий бонус

На цьому можна було б і зупинитись, але... 😉

Палітра, яку повертає ColorThief — це не варіації одного кольору, а різні кольори, які зустрічаються в зображенні. Причому вони не впорядковані за яскравістю: може бути темний, потім світлий, потім знову темний — не надто зручно, якщо ти плануєш використовувати палітру в стилях CSS.

Щоб зробити палітру придатною до автоматичного використання, виконаємо два кроки:

  1. Відсортуємо кольори за яскравістю

  2. Вирівняємо яскравість поступово — від темного до світлого

Сортування за яскравістю

Цей фрагмент ти вже бачив/ла вище:

usort($palette, function($a, $b) {
	return getLuminance($a) <=> getLuminance($b);
});

Додаємо функцію getLuminance():

function getLuminance($color) {
	// Перетворюємо hex-колір на RGB
	$r = hexdec(substr($color, 1, 2));
	$g = hexdec(substr($color, 3, 2));
	$b = hexdec(substr($color, 5, 2));

	// Використовуємо формулу для обчислення яскравості
	return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
}

Більше нічого робити не потрібно, можемо вивести палітру і глянути, що там і як. Перейди ось сюди, до робочого прикладу, і подивись на результат – під картинками верхній набір кольорів – це ті знайдені і відсортовані кольори.

Коригування яскравості

$palette = normalize_palette_lightness($palette);

А тепер найцікавіше – проганяємо всі кольори через окрему функцію (як же я без неї – кастомної функції 😂) і отримуємо повністю збалансований по яскравості набір (на сторінці із робочим прикладом це нижній набір кольорів):

function normalize_palette_lightness(array $palette): array {
	// HEX → HSL
	$hexToHsl = function($hex) {
		$hex = ltrim($hex, '#');
		$r = hexdec(substr($hex, 0, 2)) / 255;
		$g = hexdec(substr($hex, 2, 2)) / 255;
		$b = hexdec(substr($hex, 4, 2)) / 255;

		$max = max($r, $g, $b);
		$min = min($r, $g, $b);
		$l = ($max + $min) / 2;

		if ($max === $min) {
			$h = $s = 0;
		} else {
			$d = $max - $min;
			$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
			switch ($max) {
				case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
				case $g: $h = ($b - $r) / $d + 2; break;
				case $b: $h = ($r - $g) / $d + 4; break;
			}
			$h /= 6;
		}

		return ['h' => $h * 360, 's' => $s * 100, 'l' => $l * 100];
	};

	// HSL → HEX
	$hslToHex = function($h, $s, $l) {
		$s /= 100;
		$l /= 100;

		$c = (1 - abs(2 * $l - 1)) * $s;
		$x = $c * (1 - abs(fmod($h / 60, 2) - 1));
		$m = $l - $c / 2;

		if ($h < 60) {
			list($r, $g, $b) = [$c, $x, 0];
		} elseif ($h < 120) {
			list($r, $g, $b) = [$x, $c, 0];
		} elseif ($h < 180) {
			list($r, $g, $b) = [0, $c, $x];
		} elseif ($h < 240) {
			list($r, $g, $b) = [0, $x, $c];
		} elseif ($h < 300) {
			list($r, $g, $b) = [$x, 0, $c];
		} else {
			list($r, $g, $b) = [$c, 0, $x];
		}

		$r = round(($r + $m) * 255);
		$g = round(($g + $m) * 255);
		$b = round(($b + $m) * 255);

		return sprintf('#%02x%02x%02x', $r, $g, $b);
	};

	// Параметри
	$min_l = 15;
	$max_l = 95;
	$steps = count($palette) - 1;

	// Конвертуємо HEX → HSL
	$hslColors = array_map($hexToHsl, $palette);

	// Генеруємо нову яскравість для кожного кольору
	$newPalette = [];
	foreach ($hslColors as $index => $hsl) {
		$new_l = $min_l + ($index / $steps) * ($max_l - $min_l);
		$newPalette[] = $hslToHex($hsl['h'], $hsl['s'], $new_l);
	}

	return $newPalette;
}

Висновок

Працювати з бібліотекою ColorThief — приємно: вона точніша і стабільніша, ніж наш варіант на чистому PHP. Особливо приємно, що все зводиться до 2-3 рядків.

Але!

Якщо запустити обидва приклади одночасно — ти помітиш, що чистий PHP працює майже миттєво, а бібліотека — «подумає». І це при параметрі якості 10. А якщо вказати 1, скільки часу піде на 20 зображень? 😅

Тож маєш два інструменти: обирай той, який більше пасує до твого проєкту — легкість і швидкість або точність і стабільність.