Красивый вывод изображений

    image
    Всегда мне не нравилось, как выводятся изображения на моих сайтах. Хотелось какой-то упорядоченности, зависимости. Поэтому, я полез искать скрипты и решения в гугле. После безуспешных поисков, решено было делать что-то самому.
    Но что? Я стал смотреть, как сделан вывод картинок у популярных сайтов. Всё мне не нравилось, пока я не додумался посмотреть вывод в google images и google+. Меня поразило то, что я никогда этого не замечал картинки выравниваются по ширине и высоте не обрезаясь. Такой принцип я и захотел реализовать. Получается мозаика. И всё будто на своём месте.
    Не долго думая, я сел писать код, который сможет вывести картинки так же.

    Решил сделать очень просто: каждую строку делать определённой высоты и ширины. Главное, чтобы высота была не больше той, которую задаём в условиях.

    Формулы


    Вооружившись маркером и холодильником (да, нет у меня маркерной доски), я составил несколько простых уравнений и нашёл зависимость.

    h1*X1=h2*X2=...=hnXn=height (высота);
    w1*X1+w2*X2+...+wn*Xn=width (ширина);
    где
    h1-n — высоты изображний,
    w1-n — ширины изображений.
    X1-n — переменная масштабирования (если умножить на нее высоту и ширину, картинка пропорционально изменится).

    Вышло, что height=width*h1/(w1+w2*h1/h2+w3*h1/h3+...+wn*h1/hn).

    Код



    $widthdef=800; //ширина блока изображений
    $heightdef=150; //максимальная высота одной строки
    $margin=2; //отступы между картинками
    $uploadsdir='./upload/'; //папка, в которой лежат изображения
    //$img[от 1 до N] - массив картинок
    //$imagescount=N соответсвенно, количество картинок
    echo '<div style="width:'.$widthdef.'px;">'; 
    
    $first=1;
    
    while($first<=$imagescount){
    	$images=$first-1;
    	$hightes=$heightdef+1;
    		while($hightes > $heightdef && $images<$imagescount) {
    			$images++;
    			$width=$widthdef-($images-$first+1)*($margin*2); //ширина,с учетом отсупов
    			list($w[$images], $h[$images]) = getimagesize($uploadsdir.$img[$images]); //запрашиваем ширину и высоту изображения по мере необходимости
    	
    			$delim=$width*$h[$first];
    	
    			$delit=$w[$first];
    	
    			for($j=($first+1);$j<=$images;$j++) {
    				$delit=$delit+$w[$j]*($h[$first]/$h[$j]);
    			}
    			$hightes=floor($delim/$delit);//высота строки
    			
    
    			if($hightes<=$heightdef) {
    				for($i=$first;$i<=$images;$i++) {
    					$ht=$hightes.'px';
    					echo '<img style="margin:'.$margin.'px;" src="'.$uploadsdir.$img[$i].'" height="'.$ht.'">'; //выводим картинку
    					
    				}
    				$first=$images+1;
    		
    			} else { 
    
    				if($images==$imagescount) {
    			 //вывод картинок, если блок не получается полностью заполненным
    					for($y=$first;$y<=$images;$y++) {
    						echo '<img style="margin:'.$margin.'px;" src="'.$uploadsdir.$img[$y].'" height="'.$heightdef.'px">';
    					}
    					$first=$images+1; //указываем, с какой картинки считать
    				}
    	
    			}
    
    		}
    }
    
    echo '</div>';


    Если картинок не хватает до вывода по всей ширине, выводим их с максимально заданной высотой.

    От автора


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

    Еще хочется услышать другие идеи и доработки.

    PS: Код не предусмотрен для маленьких картинок(лично мне это не нужно). Если загрузить большие и маленькие, последние могут растянуться. Если кто придумает изящное решение, поделитесь.

    UPD: Спасибо за комментарии, очень полезны. Буду совершенствоваться. Ошибку поправил. Как уже сказали, все картинки пока здесь (удаляются по крону через 3 часа).
    Share post

    Comments 96

      +17
      то-же, но на жкваре
      github.com/ionelmc/jquery-gp-gallery
        0
        Да, не натыкался раньше.
          0
          Пол года назад я этим тоже интересовался: linguistic-suffocation.org/lab/tile/
          У моего алгоритма иногда бывают проблемы с последними несколькими строчками
            0
            У вас количество картинок в ряду разное, как и на G+, а у автора статьи, как я понял, это самое количество фиксировано. То есть ваш вариант как раз что надо, а у автора статьи задача изначально слишком упрощена.

            Впрочем, так совпало, что я вчера вечером занимался тем же самым и накидал свой простенький алгоритм, который делает с разным количеством картинок в ряду при определенных границах по высоте. И уже щас чужие варианты брать не хочется :)
              0
              Количество картинок подбирается под ширину блока/высоту строки.
            0
            Еще одно: эстетически идеальное решение этой проблемы нам недоступно — это 2D bin packing problem (http://en.wikipedia.org/wiki/Bin_packing_problem). Хотя у нас проблема модифицированная — мы можем пропорционально менять размер, в отличии от задач загрузки контейнеров какой-нибудь баржи или кузова фуры.
              –1
              Ну это, если честно, задачу меняет координально и ничего общего с изначальной, насколько я считаю, уже здесь нет
                +2
                Кардинально, товарищи, кардинально.
                  0
                  Да, точно, постоянно забываю =)
                    0
                    Чего минусовать-то?

                    Хабр такой хабр, что тут еще добавить.
                  0
                  Может кто-то все же скажет, почему он со мной не согласен?
                    0
                    Физически проблема другая, а математически, на мой взгляд, она упрощается дополнительной степенью свободы — параметром масштаба для каждого прямоугольника. Может статься, что она неплохо решаема.
                      0
                      Я как раз таки и имел ввиду, что эта дополнительная степень свободы слишком сильно меняет суть задачи, т.е. как мне кажется идеи из исходной задачи здесь будут не очень полезны.
              0
              Круто, я так же думал. Вы тестировали на случайных наборах? Не возникает в конце не до конца заполненых рядов?
                +1
                чуть ниже мой камент про глюк
                а про последний ряд — не вижу проблем, оно так и должно быть… не до конца заполненное
                  0
                  не, у меня чуть иначе — появляются, бывает, глюкавые несколько последних рядов
                +3
                  0
                  вы пробовали? я пробовал оба
                  добиться эффекта гугл+ не вышло
                +7
                Протестировал
                Сервер долго думал (секунд 15)
                Картинки показались, как и планировалось, выровненные
                Еще вылезла ошибка — Warning: Division by zero in /home/squie198/public_html/imagemos.php on line 160

                Лучше на javascript сделать это, пусть браузер сам думает, нечего сервер нагружать
                  –1
                  Самое интересное, что на локалке все нормально, и деления на ноль не происходит. Сейчас перепроверю.
                    +9
                    У меня уже 161, скрипт растет.
                      0
                      Пофиксил
                    –7
                    что за мода выкладывать ошибку с полным путем? Неужели нельзя оставить только имя файла и строку с ошибкой. Вы для кого полный путь до файла показываете?
                      –1
                      А почему нет собственно?
                        +1
                          0
                          Ну если не выкладывать, автор может думать, что у него этой проблемы нет, хотя она есть. Так что как раз лучше выкладывать.
                            0
                            Можно просто намекнуть же, написав что-нибудь вроде «а еще у вас вместе с ошибкой виден полный путь к скрипту».
                            +8
                            /var/www/test.php

                            Ломайте.
                              +1
                              Вы статью прочли?
                                0
                                Там в статье, кстати, рассказано как добиться ошибки, но не то, что это может за собой повлечь.
                                  0
                                  such as using the load_file() (within a SQL Injection) query to view the page source, require the attacker to have the full path to the file they wish to view
                                    0
                                    А слона я и не заметил.
                                  –1
                                  Да, прочёл, но к сути моего комментария это не относится. Как, впрочем, и все комментарии из данного дерева до 2го уровня включительно.

                                  А суть такова: паранойя в ИТ это хорошо и замечательно, но когда предоставленных кем-либо данных явно недостаточно для осуществления взлома (скажем, если человек тестировал у себя на виртуалке) либо же они публично доступны (например, демо автора) — это уже перебор. В первом случае это не опасно, а во втором любой сможет получить нужные данные.

                                  А об уязвимостях в работающих системах лучше репортить в личку, да.
                                    –1
                                    А об уязвимостях в работающих системах лучше репортить в личку, да.


                                    Вот-вот. Во-первых дадите время на закрытие (и любой уже, возможно, не сможет получить данные), а во-вторых не станете упрощать жизнь потенциальному взломщику (хоть и попахивает security through obscurity).
                          0
                          >> Лучше на javascript сделать это, пусть браузер сам думает, нечего сервер нагружать
                          Да ладно вам, человек просто набросал алгоритм на коленке на том, на чем ему удобнее. Никто не предлагает использовать это в том виде в котором оно есть.
                          +7
                          А можно было просто использовать jQuery Masonry.
                            0
                            Масонри, как по мне, не справляется с основной задачей: плотно заполнить пространство. Да и расчитан он на текстовую информацию, которая не поддается масштабировке по понятным причинам. Хотя штука достойная, я согласен
                              0
                              не совсем то, я его пытался подкрутить для вывода фоток как гугел+, выходило, что легче написать отдельный скрипт
                              начал писать, потом случайно наткнулся на тот, что указал выше
                              есть там правда баг с последней фоткой в ряду, у себя пофиксил, но корявенько, как доведу до разумного вида — обязательно закину пул реквест
                            • UFO just landed and posted this here
                                –9
                                1) Было интересно реализовать, как минимум.
                                2) Для простоты, не хотел усложнять код.
                                3) Вывел ключевой момент кода, в котором идет подсчет. По ссылке есть полное демо.
                                4) Не привык я к IDE, работаю в блокноте, по старинке.
                                • UFO just landed and posted this here
                                    0
                                    Я принимаю замечания, просто не все. Спасибо.
                                    +22
                                    4) долго плакал… Милейший, вам памятник нужно воздвигнуть, и почетное звание: «Увидел Ад, и не устрашился».
                                      –3
                                      Возможно. Туда-сюда меня с vim на gedit кидает.
                                      +1
                                      > работаю в блокноте
                                      Самое ужасное для меня при работе с блокнотом — было перебивание табуляции. Что-то поменял, и все: во имя красивого оформления перебивай n-строчный блок на один-два отступа вправо/влево. Не сильно замечал, но когда перешел на notepad++, как в раю оказался.
                                        +17
                                        Уф. А сколько вас еще ждет открытий чудных…
                                          +10
                                          Я вот тоже стирал ручками, но самое ужасное для меня, это то, что стираются руки до крови. Теперь использую стиральную машину:

                                          image

                                          Руки не стираются, но всеравно приходится следить за временем стирки, вынимать белье со стекающей водой, отжимать руками его. В общем надо то-то оптимизировать.
                                            +2
                                            Нужно апгрейдить до современной стиралки. Засыпал порошок, нажал на кнопочку. Все само стирается, отжимается. Но все равно приходится гладить рубашки:( Надо подключать компонент «wife».
                                              0
                                              вы не програмист, вы сисадмин =)
                                                0
                                                гладить != стирать ;)
                                                Это уже другая процедура, отдельная тема для оптимизации и автоматизации процесса.
                                            0
                                            4) а Вы, простите, стирает одежду ручками в тазике, как это делали наши деды с бабками? Попробуйте контрольный вам, и тот удобнее.
                                              +1
                                              Простите, писал с андроида, он как обычно выдумывает предложение, имелось в виду,
                                              Попробуйте консольный вим, и тот удобнее
                                              +5
                                              1. Посмотрите, как гугл картинки выглядят без скрипта, как в 00-х ) Даже корпорация добра не делает это на серверах, ибо не выгодно.
                                              0
                                              1. О посетителях без анлимов некоторые программеры забывают, а жаль — таких очень много
                                              2. Это тестовый код, зачем здесь что либо выносить?
                                              3. Код оформлен в виде кода, который нормальный программер вполне чегко прочитает — что еще надо?
                                              4. И что что не определены переменные? Это ПЫХ, можно не заморачиваться с мелочами

                                              Удачи в дальнейшем развиитии программирования — статус нуба — это поправимо ;)
                                              0
                                              Спасибо комментирующим) Заложил даже больше ради комментов, чем оригинального решения) Хотя автору тож спасибо)
                                                +1
                                                100500 Warning'ов, да и думает очень долго, а я всего лишь 10 картинок добавил…
                                                  +2
                                                  Разве float: left + fixed height не решит проблему или я что-то не понял?
                                                    0
                                                    Вы почти правы — намного проще и почти тот же результат. Только вот в мелочах (в данном случае — неровный правый край) и прячется качество. Согласитесь — выдача поиска картинок от Гугла была бы менее симпатичной в таком варианте.
                                                      +2
                                                      display: inline;
                                                      text-align: justify;
                                                        +1
                                                        Тогда маргины будут разные у картинок, тоже не красиво.
                                                    +1
                                                    Сервер будет плакать кровавыми слезами от такого скрипта.
                                                      +6
                                                      Собственно вот:
                                                      Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 20000 bytes) in /home/squie198/public_html/imagemos.php on line 88
                                                      
                                                      +7
                                                      А тут можно посмотреть, чем кормят скрипт: squier13.ru/upload/.
                                                        +1
                                                        Блин какая радость =) А вот автор мог бы предугадать это и сделать удаление картинок после ухода со страницы или обновления
                                                          +2
                                                          Автор наверняка проводил эксперимент с сообществом — интересовался, чего ему там закачают. Либо уже все знал, и просто хотел создать/пополнить свою коллекцию пор… Котят!
                                                            0
                                                            для таких целей достаточно хотя бы положить пустой index.php дабы запретить сторонний листинг папки.
                                                              +1
                                                              Тогда уж index.html, а то и просто .htaccess c deny for all.
                                                                0
                                                                PHP прогеру index.php =) как показывает практика они всегда делают на *.php то, что можно сделать и на обычном *.htnl.

                                                                Конечно же .htaccess правильнее, но я говорил о том, что проще в количестве потраченный секунд, на минимальную защиту. чистый сарказм не более.
                                                                  0
                                                                  Они — это я тоже :)
                                                                0
                                                                для таких целей предназначен htaccess
                                                              –2
                                                              Специально не скрывал)
                                                              +1
                                                              в общем — есть предложение!
                                                              В связи с наступлением НГ2012 — собрать медиа хабра коллекцию картинок.
                                                              Т.е. народ присылает картинки на некий ресурс символизируя позитив — остальные могут просматривать оную этот позитив приумножая. Коллекция не будет аналогом гугла так как выборка будет создаваться именно людьми а не поисковиком.
                                                                +1
                                                                20 случайных выбраных кликов, ни одних сисек, я удивлен
                                                                  0
                                                                  +4
                                                                  Очевидно же, что с javascript это сделать лучше
                                                                    +1
                                                                    честно говоря текст даже не читал, просматриваю камменты для пополнения базы jquery плагинов :)
                                                                    +2
                                                                    Ну прям уж фантастически сложные формулы… Топик ни о чем.

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

                                                                    По коду было уже достачно замечаний выше, добавить особо нечего.
                                                                      +1
                                                                      Собственно что мешает вам самому «прикрутить пару эвристик» и выложить сие на всеобщее обозрение? Или только говорить мастер?
                                                                        –1
                                                                        Насколько я помню, в проектах я с подобной подзадачей не сталкивался, а писать только для того чтобы на хабр запостить не очень хочется. Да и к тому же не такая уж и интересная проблема: идеи и так всем понятны, а реализация может зависеть от того, как и где это применяется.
                                                                      –2
                                                                      есть такая вещь замечательная — кроп
                                                                        +3
                                                                        И именно для того, чтоб его избежать, и создан этот топик.
                                                                        +1
                                                                        Warning: Division by zero in /home/squie198/public_html/imagemos.php on line 161
                                                                          +7
                                                                          И этот топик в «лучшее за 24 часа». Я сам пхп-быдлокодер и обычно плюсую топики в блоге PHP почти их не читая. Но не настолько же, чтобы эхать тег img и вычислять его width и height, да ещё в style без ресайза собственно картинки через imagemagik или gd…

                                                                            +3
                                                                            А представьте, чем будет рыдать сервер, если ему перед отрисовкой (ибо сортировка может менятся и заранее не выйдет) страницы еще и меджить картинка.
                                                                              0
                                                                              В чём она может меняться при использовании в качестве галереи? Загрузили новый файл в альбом, пересчитали, отресайзили, и лежит до следующей загрузки или очистки кэша.

                                                                              Рандомный вывод или вывод результатов поиска — согласен, но тут уж, имхо, вообще на клиенте ресайзить надо. Рандом и поиск и так тяжелые операции, чтоб их чем-то ещё нагружать.
                                                                            +2
                                                                            В Google Images они все-таки обрезаются. Как же иначе ранжировать их по значимости?
                                                                              0
                                                                              Гугл, видимо, при индексации их режет.
                                                                              0
                                                                              Пушистые нямки на главной!
                                                                                +1
                                                                                Котики на Хабре!
                                                                                  +2
                                                                                  Не понимаю, как вообще могла прийти в голову идея делать это на серверсайде. Ну и код конечно не ахти: лапшичка, да с зашитой логикой представления.

                                                                                  Автор хотел услышать идеи и доработки — я бы советовал хотя бы этот код переписать по-человечески, ну а в продакшене использовать клиент-сайд решения для этой задачи.
                                                                                    +1
                                                                                    Я пользуюсь либой code.google.com/p/phpresizer/ тоже самописанная
                                                                                    Думаю автор сможет от туда много чего себе перенять или просто взять эту либу на вооружение.

                                                                                    1. Поддерживает кеширование откадрированных изображений,
                                                                                    2. работает с полупрозрачными GIF, PNG, JPG

                                                                                    2. Поддерживает движки:
                                                                                    GD (Graphics Draw) library
                                                                                    ImageMagick
                                                                                    GraphicsMagick

                                                                                    3. При кадрированиии можно управлять параметрами:
                                                                                    «width»=>170, // желаемая ширина аватарки
                                                                                    «height»=>210, // желаемая высота аватарки
                                                                                    «aspect»=>false, // сохранять ли пропорции исходного изображения
                                                                                    «crop»=>95, //часть центральной части которую следует увеличить (прозумировать центр)
                                                                                    «quality» => 75 // качество JPEG
                                                                                    «background» => «ff00ae» // цвет котором стоит залить свободные области при $aspect = true;
                                                                                    'zoomSmallImage'=>false, // стоит ли ресайзить маленикие изображния
                                                                                    'pngCompress' => 9 // степень компрессии для png
                                                                                      0
                                                                                      Есть одна тема для tumblr, там фиксированная ширина картинок, но разная высота, и все подгружается по мере прокрутки страницы. В результате, все очень просто реализовано и без обрезок (простое уменьшение до одинаковой ширины). Хотя, не позволяет избавится от «рваного» края, но делает его эстетически привлекательным просто используя низ, а не левую сторону.
                                                                                        +1
                                                                                        Можно немножко усложнить вам задачу. Бывают фотки слишком растянутые в ширину или в высоту. На том же G+ такие фотки кропятся, чтобы фотка не выходила за определенные границы соотношений w/h и h/w. Представьте что кто-то загрузил фотку 1000x100, или наоборот 100x1000. Вот такие как раз предварительно кропятся до определенных границ (скажем, 400x100 и 100x400), а потом уже в дело входит тот самый алгоритм, про который статья.
                                                                                          +1
                                                                                          А зачем тут php?
                                                                                            +2
                                                                                            php — это ж наше всё. Как то видел клас для прожига дисков. И напишут великие умы свою операционную систему на php, где вместо биоса будет подыматься апач, либо нгинкс. И придет недалекое светлое будущее, и все будут жить в мире и гармонии с окружающим миром. конец пророчества.
                                                                                            0
                                                                                            когда-то писал давно нечто подобное: jQuery плагин Кирпичи

                                                                                            Only users with full accounts can post comments. Log in, please.