Наверх

Галерея в стиле Вконтакте или Яндекс.Картинок

Уже написал две статьи о своем новом сниппете AlignImage на community.modx-cms.ru и на modxclub.ru. Для тех, кому интересны подробности, опишу их здесь.

Сниппет позволяет выводить галерею в стиле Вконтакте или Яндекс.Картинок. Вот так, например:



Расскажу, как я этого добился и немного прокомментирую код сниппета.

Алгоритм ресайза изображений


Картинки по условиям обрезать нельзя, но они все равно должны быть одинаковыми по высоте и помещаться ровно в заданную ширину. Сразу скажу, что весь секрет в том, чтобы подобрать такую высоту картинок, чтобы при ресайзе до этой высоты всех картинок в строке их общая ширина была равна конкретному значению.

Давайте посмотрим, как высчитывается нужная высота картинок.

Сначала нужно узнать, сколько картинок будет в каждой строчке. Это количество мы узнаем от пользователя, который при вызове сниппета указывает параметр &lineLimit.

Дальше мы ресайзим все картинки в строке по наименьшей высоте, чтобы выровнять их. Например так:



После этого находим суммарную ширину получившихся картинок в строке:


Width = W_1 + W_2 + W_3

И находим нужную высоту умножением минимальной высоты картинок в строке на коэффициент ресайза, который равен:
&lineWidth / Width
где &lineWidth — это требуемая ширина строки (которую задает пользователь при вызове сниппета), а Width — найденная в предыдущем шаге общая ширина.
Height = Height_Min * &lineWidth / Width


Реализация алгоритма в коде


Получаем картинки, разбитые на линии:
$i = 0;
$Lines = array(); // каждый элемент этого массив - строка картинок
$isItems = true; // проверяем, не кончились ли картинки
while ($isItems) {
  $scriptProperties['outputSeparator'] = ',';
  $scriptProperties['limit'] = $lineLimit;
  $scriptProperties['offset'] = $i;
  
  // сколько всего картинок будет после текущей итерации
  $subTotal = $scriptProperties['offset'] + $lineLimit;
  
  // если предсказывается превышение лимита
  if ($limit && $subTotal > $limit) {
    /* изменяем лимит — например, при выводе пяти картинок по 3 в строке
       в последней итерации лимит должен быть равен 2, а не 3) */
    $scriptProperties['limit'] = $limit - $scriptProperties['offset'];
  }
  
  // получаем список картинок для этой строки
  $result = $modx->runSnippet($snippet, $scriptProperties);
  
  // если картинки кончились, выход из цикла
  if (!$result) break;
  
  // превращаем список в JSON-массив
  $Lines[] = '{' . $result . '}';
  
  // проверяем, все ли картинки вывелись
  $i = $i + $lineLimit;
  if ($i >= $limit) $isItems = false;
}

И обрабатываем каждую строку с помощью нашего алгоритма:
foreach ($Lines as $line) {
    // получаем обычный массив картинок
    $line = $modx->fromJSON($line);
    $images = $w = $h = array();
	
    // вычисляем размеры каждой картинки
    foreach ($line as $id => $img) {
        // убираем слеш в начале пути
        if (substr($img,0,1) == '/') $img = substr($img,1);
		
        // если указано, что нужно обрезать лишний фон, выполняем
        if ($crop) {
        // используем для этого Imagik - в MODX есть по умолчанию
          $im = new Imagick($img);
		  
          // обрезаем фон
          $im->trimImage(0);
		  
          // готовим новое имя картинки, чтобы сохранить ее
          $path_info = pathinfo($img);
          $cropedFile = $path_info['dirname'].'/croped-'.$lineWidth.'/';
          if (!file_exists($cropedFile)) mkdir($cropedFile);
          $cropedFile .= $path_info['basename'];
		  
          // и, собственно, сохраняем
          $im->writeImage($cropedFile);
          $img = $cropedFile;
        }
		
        // узнаем высоту и ширину картинки
        $size = getimagesize($img);
        $w[] = $size[0];
        $h[] = $size[1];
        $images[$id] = $img;
    }
	
    /* ресайзить картинки по наименьшей высоте не будем — вдруг, потом
       придется их обратно увеличивать? Просто высчитываем,
       какой была бы ширина отресайзенных картинок */
    foreach ($w as $k => $w_old) {
        $w[$k] = floor($w_old * min($h) / $h[$k]);
    }
	
    // высчитываем требуемую высоту
    $lineHeight = floor(min($h) * $lineWidth / array_sum($w));
	
    // и ресайзим картинки по этой высоте
    foreach ($images as $id => $image) {
        $ph = $tvPrefix.$processImage;
        $im = new Imagick($image);
        $im->resizeImage(0,$lineHeight,0,1);
        // новое имя картинки, уже отресайзеной
        $path_info = pathinfo($image);
        $image = $path_info['dirname'].'/h-'.$lineHeight.'/';
        if (!file_exists($image)) mkdir($image);
        $image .= $path_info['basename'];
        // сохраняем картинку
        $im->writeImage($image);
        // и показываем пользователю
        $output .= $modx->getChunk($template,
             array($ph => $image
                 , 'id' => $id
                 , 'album' => $scriptProperties['album']))."\n";
    }
}

return $output;

Результат




И еще совет напоследок — устанавливайте значение параметра &lineWidth на 10-15 пикселей меньше контейнера, чтобы между изображениями было расстояние и картинки не «слипались». G+

UPD. Pashkevich Aleksandr переработал компонент, выложил его в репозиторий MODX. Описание.

12 комментариев

  1. Pashkevich Aleksandr 22 мая 2013, 11:55(Комментарий был изменён) # +1
    Спасибо за скрипт, мне тут внезапно понадобилось его использовать для галереи на 200+ изображений. Он не успевал по таймауту времени. Я немного модифицировал его, если интересно могу выложить свою модификацию кода. Так же там с подгоном под Bootstrap Gallery
    1. Илья Уткин 22 мая 2013, 13:29(Комментарий был изменён) # 0
      Вообще супер) Сделайте форк на гитхабе, вставьте туда измененный код и скиньте ссылку на репозиторий, чтобы те, кто заходят на сайт видели, что есть модификации)
      1. Alex 03 апреля 2014, 18:44 # 0
        А где на гитхабе можно посмотреть, где отметка что форкнули?
        1. Илья Уткин 03 апреля 2014, 18:51 # 0
    2. Mackay Bravo 10 июля 2013, 15:25(Комментарий был изменён) # 0
      Спасибо автору!
      1. Владимир 28 марта 2015, 15:14 # 0
        Доброго дня!
        Для ms2Gallery не появлялось готового решения?
        А вдруг?)))
        1. Илья Уткин 30 марта 2015, 07:35 # 0
          Нет, не появилось, так как ms2Gallery ресайзит фотки при загрузке, а для реализации алгоритма необходимо ресайзить фотки в зависимости от их порядка и взаимного расположения.
          1. Владимир 30 марта 2015, 07:39(Комментарий был изменён) # 0
            Спасибо. В ms2Gallery есть ведь и оригиналы изображений. Я уже пробовал ресайзиить оригиналы в ms2Gallery. Т.е. создаются еще одни превьюшки, а ms2Gallery используется для привязки к ресурсу и хранения.
            Т.е., ну создает свои превьюшки ms2Gallery и пусть создает, можно в источнике указать минимальное количество ее собственных превьюшек.
            1. Илья Уткин 30 марта 2015, 07:53 # 0
              А вот этот сниппет не пробовали (описание)? По идее он должен нормально отработать с любым сниппетом галереи — хоть Gallery, хоть ms2Gallery, хоть MIGX…
              1. Владимир 30 марта 2015, 07:55 # 0
                Да-да, им и пробовал.
        2. Виталий Б. 06 июня 2016, 13:13(Комментарий был изменён) # 0
          Приветствую. У меня такой вопрос: можно ли как-то ресайзить изображения с внешних ресурсов и вставлять? Получаю по VK API массив фотографий с их размерами и хотел бы из них сделать динамическую галерею (рандомные альбомы из списка и рандомные фото из них), но на сайте эти фото не хранить.
          ...
          $photosObj = $modx->vk->api('photos.get', array(
              'album_id' => $album,
              'owner_id' => $owner,
              'count' => $limit,
              'photo_sizes' => '1'),$https)[response];
          
          if (!empty($photosObj) && count($photosObj) > 0) {
              $photos = array();
              foreach ($photosObj[items] as $item) // тут просто собираю нужные мне поля
              {
                  $photos[] = array(
                      'caption'       => $item[text], //подпись
                      'thumb'         => $item[sizes][$thumbSize][src], //картинка-превью
                      'thumbHeight'   => $item[sizes][$thumbSize][height], //высота превью
                      'thumbWidth'    => $item[sizes][$thumbSize][width], //ширина превью
                      'photo'         => $item[sizes][$bigSize][src], //картинка-оригинал
                      'photoHeight'   => $item[sizes][$bigSize][height], //высота оригинала
                      'photoWidth'    => $item[sizes][$bigSize][width]); //ширина оригинала
              }
              
              $Lines = array_chunk($photos, $lineLimit); //делю на строки
          ...
          
          Или это лучше делать на JavaScript?
          1. Илья Уткин 13 июня 2016, 11:45 # 0
            Я точно не знаю, но мне кажется, что всё зависит от настроек сервера. Если на сервере разрешено ресайзить сторонние картинки, то все получится. Но в любом случае — можно же сохранять у себя картинку, а потом ресайзить.

          Авторизация

          через сервис Loginza:

          Подписка или RSS

          Буду присылать новые статьи — никакого спама



          Шаблоны MODX

          1 2 Дальше »

          Объектная
          модель
          MODX