Як визначити домінантний колір зображення в Laravel

Перекладено ШІ 0 Laravel News 07 серпня, 2026

Laravel представив метод dominantColor() для створення естетичних кольорових плейсхолдерів, які заповнюють простір до повного завантаження фотографій. Дізнайтеся, як за допомогою лише одного рядка коду покращити візуальний досвід користувачів та позбутися порожніх блоків на сторінці.

Зображення, яке ще не завантажилося, залишає на сторінці порожнечу. Зазвичай це вирішують за допомогою сірих прямокутників, розмитих мініатюр (blurhash) або просто залишають пусте місце. Простіший, але ефективний спосіб — заповнити простір середнім кольором фото. Так макет виглядатиме завершеним ще до того, як з'явиться перший піксель справжнього зображення.

Laravel 13.24 додав метод dominantColor() до свого native image API. Це дозволяє отримати потрібний колір без встановлення додаткових пакетів. У цій статті ми розберемося, як дізнатися цей колір, де його зберігати та як використовувати.

# Налаштування

Драйвери GD та Imagick для роботи із зображеннями використовують Intervention Image. Це рекомендована залежність, яку потрібно встановити вручну:

composer require intervention/image:^4.0

Більше нічого не потрібно — визначення домінантного кольору працює на обох драйверах.

# Отримання домінантного кольору

Викличте dominantColor() на будь-якому екземплярі зображення, щоб отримати hex-рядок:

use Illuminate\Support\Facades\Image;
 
$color = Image::fromPath(storage_path('app/photo.jpg'))->dominantColor();
// "#8a6f4c"

Метод працює так: він створює копію зображення, стискає її до одного пікселя та зчитує його значення. Це саме середній колір, а не той, що зустрічається найчастіше. Наприклад, фото червоної комори на зеленому полі дасть брудно-оливковий колір, а не червоний. Для заповнювача (placeholder) це ідеально, адже колір має слугувати фоном для всього зображення, а не виділяти окремий об'єкт.

Результат завжди містить префікс решітки (#). Зображення з alpha channel повертають восьмизначний код (#rrggbbaa) — врахуйте це при виборі розміру колонки в базі даних.

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

$color = Image::fromStorage('uploads/photo.jpg')
    ->crop(800, 600, x: 200, y: 0)
    ->dominantColor();

Відріжте небо — і колір зміниться.

# Зберігання кольору разом із завантаженням

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

Schema::create('photos', function (Blueprint $table) {
    $table->id();
    $table->string('path');
    $table->string('dominant_color', 9)->nullable();
    $table->timestamps();
});

У контролері створіть зображення та використовуйте цей екземпляр повторно:

use App\Models\Photo;
 
public function store(Request $request)
{
    $request->validate([
        'photo' => ['required', 'image', 'max:12288'],
    ]);
 
    $photo = $request->image('photo')
        ->orient()
        ->scale(width: 1600)
        ->optimize();
 
    return Photo::create([
        'path' => $photo->store('photos'),
        'dominant_color' => $photo->dominantColor(),
    ]);
}

Тут важливий порядок: store() виконує всі операції, кешує оброблені байти в екземплярі та очищує конвеєр (pipeline). Наступний виклик dominantColor() працює вже з цими закешованими даними без зайвих зусиль. Якщо поміняти рядки місцями, колір буде розраховано на тимчасовому клоні, а store() знову оброблятиме зображення. Результат той самий, але роботи вдвічі більше.

Значення також мемоїзується всередині екземпляра, тому повторні виклики dominantColor() безкоштовні. Оскільки кожна трансформація повертає новий екземпляр, мініатюра, створена з того ж джерела, розрахує свій власний колір:

$source = Image::fromStorage('uploads/photo.jpg')->orient();
 
$thumbnail = $source->cover(300, 300);
$display = $source->scale(width: 1600);
 
$thumbnail->dominantColor();  // розраховано для кропнутого квадрата
$display->dominantColor();    // розраховано для повного кадру

# Використання кольору як placeholder

Коли колір збережений у моделі, шаблон Blade перетворюється на блок із заданим фоном та правильним співвідношенням сторін (aspect ratio):

<div class="aspect-[3/2] overflow-hidden rounded-lg"
     style="background-color: {{ $photo->dominant_color }}">
    <img src="{{ Storage::url($photo->path) }}"
         alt="{{ $photo->caption }}"
         loading="lazy"
         class="h-full w-full object-cover">
</div>

Кольоровий блок з’являється миттєво, а зображення плавно завантажується поверх нього. Це ідеально поєднується з loading="lazy" у великих галереях, де картинки підтягуються лише під час скролу.

# Читабельність тексту поверх кольору

Підписи або іконки поверх заповнювача мають залишатися читабельними незалежно від того, що на фото: снігове поле чи нічне небо. Розрахунок відносної яскравості (relative luminance) допоможе визначити поріг у кілька рядків:

namespace App\Support;
 
class Color
{
    public static function isLight(string $hex): bool
    {
        [$r, $g, $b] = sscanf(substr($hex, 0, 7), '#%02x%02x%02x');
 
        // Яскравість Rec. 709, діапазон 0-255.
        return (0.2126 * $r + 0.7152 * $g + 0.0722 * $b) > 140;
    }
}
<figcaption class="{{ Color::isLight($photo->dominant_color) ? 'text-gray-900' : 'text-white' }}">
    {{ $photo->caption }}
</figcaption>

# Заповнення полів при масштабуванні та повороті

Це ж визначення кольору можна використовувати прямо під час трансформацій. Передача аргументу dominant у метод contain() заповнить пусті поля кольором самого зображення замість фіксованого значення:

Image::fromUpload($request->file('photo'))
    ->contain(1200, 800, background: 'dominant')
    ->store('photos');

Вертикальне фото в горизонтальному контейнері отримає поля, що пасують до нього за тональністю — це виглядає набагато краще в сітці, ніж стандартні чорні чи білі смуги. Метод rotate() працює аналогічно для кутів, що з’являються при повороті:

$image->rotate(8, background: 'dominant');

# Чим цей метод не є

dominantColor() повертає лише один усереднений колір. Це не екстрактор палітри, він не знайде акцентний колір, який би обрав дизайнер. Якщо вам потрібен набір характерних зразків або кластеризація, яка ігнорує фон, краще використовувати спеціалізовані бібліотеки.

Але для створення заповнювачів, фону для полів та стилізації карток під колір контенту — одного середнього кольору цілком достатньо. І тепер це частина фреймворку.

# Додаткові матеріали

Популярні

Інше, що варто прочитати

19 Оновлено 26 червня, 2026

Intervention Image: потужний інструмент для роботи з зображеннями у Laravel

Досліджуйте потужний пакет Intervention Image для PHP, який виводить редагування зображень на новий рівень з оновленою версією 3. Чи готові ви дізнатися, які нові можливості та функції чекають на вас у цьому інструменті

15 Оновлено 26 червня, 2026

Як задокументувати кілька API в Laravel за допомогою Scramble

Ви знали, що в одному додатку Laravel можна реалізувати кілька API? У нашій статті ви дізнаєтеся, як за допомогою Scramble легко документувати різні версії API та налаштувати доступ до документації, щоб зробити її публічною або приватною. Читайте далі, щоб дізнатися більше

16 Оновлено 26 червня, 2026

Простий пакет RabbitMQ для Laravel

Вам цікаво дізнатися, як спростити інтеграцію RabbitMQ у вашому Laravel-додатку? У нашій статті ми розглянемо пакет Simple RabbitMQ, який дозволяє легко налаштувати багатозʼєднання, публікувати повідомлення та обробляти черги за допомогою простого синтаксису. Читайте далі, щоб дізнатися більше!