Статья

Оптимизация изображений в Sngine: Как уменьшить вес фото в 15 раз без потери качества

1. Проблема: Качество убивают дважды

Если вы владелец сайта на движке Sngine, вы наверняка сталкивались с ситуацией, когда пользователи жалуются на «мыло» и пикселизацию на фотографиях. Особенно обидно, когда человек загружает шикарный снимок с камеры на 12–15 Мб, а система превращает его в нечто ужасное.

Почему так происходит?

По умолчанию Sngine работает по стандартному алгоритму:

  1. Он не меняет физические размеры изображения (ширину и высоту).

  2. Он сжимает качество (уровень JPEG).

Представьте: пользователь загружает фото 6000×4000 px (12 Мб). Система «ресайзит» его, оставляя те же 6000×4000 px, но просто «выкидывая» цветовую информацию. В итоге:

  • Размер файла становится чуть меньше (но не критично).
  • Картинка превращается в пиксельную «лестницу» на каждом углу.
  • Страница грузится долго из-за огромного полотна.

Нам показалось это нелогичным. Зачем хранить изображение с бешеным разрешением, если оно всё равно отображается в ленте шириной 800px?


2. Решение: Атакуем разрешение, а не качество

Мы пошли другим путем. Вместо того чтобы убивать качество при огромном разрешении, мы уменьшаем само разрешение до разумных пределов, сохраняя при этом оригинальные пропорции и четкость.

Логика простая: Сделать максимальную сторону изображения не более 1000 пикселей.

Это магическое число, которое обеспечивает: * Идеальный вид на мобильных устройствах и десктопах. * Сохранение естественной резкости (без пикселизации). * Уменьшение веса в 10–15 раз.

Что мы сделали с кодом?

Мы модифицировали системный файл class-image.php. Теперь при загрузке картинка скейлится не «в ширину экрана», а ограничивается по высоте (или ширине) до 1000px.

Пример из жизни:

  • Было: 6000×4000 px | 12 Мб * Стало: ~1500×1000 px (пропорционально) | 0.8 Мб
  • Разница: вес меньше в 15 раз! Качество — как при просмотре на ретина-экране.

3. Инструкция по внедрению

Внимание! Перед редактированием файлов обязательно сделайте резервную копию сайта и базы данных.

  1. Откройте файл /includes/class-image.php
  2. Перепишите функцию public function save, заменив код тем, что указан ниже.
  3. Проверьте, что есть закрывающие скобки } - их должно быть в конце 4 штуки (в самом файле class-image.php, а не в нашем коде)!

/**
   * save
   * 
   * @param string $path
   * @param string $quality (high|medium|low)
   * @param int|null $maxSize Максимальное значение для ширины/высоты (по умолчанию 1000px)
   * @return void
   * @throws Exception
   */
  public function save($path, $quality = 'medium', $maxSize = 1000)
  {
    // Подготовка директории
    if (!file_exists(dirname($path))) {
      mkdir(dirname($path), 0777, true);
    }

    // Настройки качества
    switch ($quality) {
      case 'high':
        $quality = 100;
        $compression = 1;
        break;
      case 'low':
        $quality = 50;
        $compression = 7;
        break;
      default:
        $quality = 85;
        $compression = 3;
        break;
    }

    // Текущие размеры
    $currentWidth = $this->getWidth();
    $currentHeight = $this->getHeight();
    $imgToSave = $this->_img;

    // Проверяем, нужно ли масштабировать
    if ($currentWidth > $maxSize || $currentHeight > $maxSize) {
      // Вычисляем коэффициент масштабирования
      $ratio = min($maxSize / $currentWidth, $maxSize / $currentHeight);
      $newWidth = (int)($currentWidth * $ratio);
      $newHeight = (int)($currentHeight * $ratio);

      // Создаем новое изображение
      $resizedImg = imagecreatetruecolor($newWidth, $newHeight);
      
      // Сохраняем прозрачность для PNG
      if ($this->_img_type == 'image/png') {
        imagealphablending($resizedImg, false);
        imagesavealpha($resizedImg, true);
      }

      // Масштабируем с ресамплингом
      imagecopyresampled(
        $resizedImg, $this->_img,
        0, 0, 0, 0,
        $newWidth, $newHeight,
        $currentWidth, $currentHeight
      );
      
      $imgToSave = $resizedImg;
    }

    // Сохранение в зависимости от типа
    try {
      switch ($this->_img_type) {
        case 'image/jpeg':
        case 'image/jpg':
          if (!imagejpeg($imgToSave, $path, $quality)) {
            throw new Exception(__("Failed to save JPEG image"));
          }
          break;
        case 'image/png':
          imagealphablending($imgToSave, false);
          imagesavealpha($imgToSave, true);
          if (!imagepng($imgToSave, $path, $compression)) {
            throw new Exception(__("Failed to save PNG image"));
          }
          break;
        case 'image/gif':
          if (!imagegif($imgToSave, $path)) {
            throw new Exception(__("Failed to save GIF image"));
          }
          break;
        case 'image/webp':
          if (!imagewebp($imgToSave, $path, $quality)) {
            throw new Exception(__("Failed to save WebP image"));
          }
          break;
        default:
          throw new Exception(__("Unsupported image type: {$this->_img_type}"));
      }
    } finally {
      // Очищаем временное изображение если оно создавалось
      if (isset($resizedImg) && $resizedImg !== $this->_img) {
        imagedestroy($resizedImg);
      }
    }
  }

4. Финальная настройка

  1. Прейдите в Админ-панель → Настройки → Загрузки и выберите Качество фотографии: Фотографии среднего качества со средним сжатием

123.png

  1. После всех манипуляций обязательно очистите кэш браузера и кэш Sngine (Админ-панель → Инструменты → Уборщик мусора → Удаление скомпилированных файлов шаблонов).

5. Результат: Почему это круто

  1. Экономия дискового пространства до 90%. Ваш хостинг скажет вам спасибо.
  2. Скорость загрузки страниц вырастает в разы (меньше мегабайт скачивается).
  3. Google PageSpeed будет любить ваш сайт за правильную работу с изображениями.
  4. Никакой пикселизации. Глаз видит четкую картинку, а не артефакты сжатия.

Единственное ограничение: если на вашем сайте пользователям нужно приближать (зумить) изображения до 100% масштаба, чтобы рассмотреть мелкие детали (например, вы продаете запчасти или арт), этот метод не подойдет. Но для 90% социальных сетей, блогов и досок объявлений — это идеал.

1 Ответ

  1. Сделал. Спасибо!