Фотогалерея или каталог изображений - это список маленьких картинок, при щелчке по которым мы видим большие, подробные изображения. Его можно сделать попроще (80 строк кода) или посложнее, с более полной функциональностью (130 строк).

Галерея изображений

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

Большую картинку чаще всего можно увидеть на сайте в двух вариантах: "плавающую" поверх страницы с затемнённым фоном или закреплённую на странице в определённом месте – например, на "слайдере", в котором картинки могут сменяться в том числе и автоматически.

Около большой картинки – справа и слева – обычно располагают стрелочки, позволяющие "листать" изображения. Стрелка – это ссылка, при щелчке по которой происходит то же самое, что и при щелчке по миниатюре в списке – открывается соответствующее большое изображение (в случае стрелки – следующее за текущим в списке картинок). Стрелки не должны мешать рассматривать изображение. Обычно они еле заметные, маленькие; но сама область ссылки на следующее изображение большая – часто она занимает половину большого изображения. То есть если вы случайно щёлкните мышкой по любому месту большой картинки, откроется следующая большая картинка (или предыдущая, если щёлкнуть левее).

Это не очень правильное поведение, его уже можно считать в какой-то степени навязанным пользователю. Поэтому в нашей галерее стрелки (область ссылки) достаточно ограниченного размера – 100-200 пикселов, а при щелчке по основной части большого изображения оно закрывается. Чтобы это не было неожиданным для пользователя, курсор имеет вид "уменьшающей лупы" (а при наведении на стрелки – обычный "указатель"-pointer).

Размер большого изображения (точнее, области вместе со стрелками) мы делаем максимальным по размеру экрана – примерно 85%, а картинка "вписывается" в эту область по ширине и высоте. Тут могут быть варианты, если известны пропорции большинства изображений в галерее, вытянутые вертикально или горизонтально, – можно область тоже вытягивать соответственно.

Хранение файлов

Говорят, что много файлов в одной папке замедляет работу файловой системы. Но сколько это – "много"? Наш опыт показывает, что несколько тысяч файлов или несколько сотен – нет существенной разницы. Поэтому имена файлов у нас не генерируются генератором и не распределяются по папкам – aab, aac – а тупо 1.png, 2.png, 3.png... Файлы до номера 1000 хранятся в папке "0", файлы от 1000 до 2000 – в папке "1", и так далее. Ходят слухи, что случайные имена файлов нужны ещё и для какой-то "защиты". Возможно, от скачивания? Но современные средства позволяют выкачивать целые сайты без больших проблем.

Файлы больших изображений могут быть какими угодно, файлы миниатюр – только png. Миниатюры хранятся точно с такими же номерами и в таких же папках, только внутри папки th – от слова "thumbnail". Зная эту систему, можно легко получить ссылку на миниатюру для большого изображения, но нельзя однозначно получить ссылку на большое изображение из миниатюры – потому что у большой картинки могут быть разные расширения: png, jpg, jpeg... Поэтому при генерации списка миниатюр на сервере мы должны в каждом элементе списка как-то передать ссылку на большое изображение.

Обычно это делается так: миниатюра оборачивается в ссылку <a href="files/0/1.jpg"><img src="files/th/0/1.png"></a>. А мы делаем короче: <img src="files/th/0/1.png" alt="files/0/1.png">.

Взаимодействие сервера с javascript

Давно прошли те времена, когда на сервере в html писали <img onclick="showBigImage(this)">. Всё чаще можно встретить обратную ситуацию – с сервера отправляются голые данные в JSON, а весь HTML генерируется в браузере. У нас в этом плане достаточно консервативная схема: html всего списка картинок генерируется на сервере. А для javascript мы передаём специальные сигналы и метки. Контейнер со списком миниатюр получает уникальный идентификатор по имени обработчика – в нашем случае imgList: <div id="imgList1">тут список картинок</div> Если наборов изображений на странице несколько, они будут иметь отдельные идентификаторы – imgList2, imgList3... Об это должен позаботиться сервер. В конце html-страницы мы вызываем нужное количество обработчиков с параметрами:

Договорённости сервера с клиентом такие:

  1. картинки-миниатюры в контейнере должны иметь атрибут class="thumb";
  2. статическая большая картинка (если она есть) должна иметь идентификатор в соответствии с контейнером миниатюр: <div id="static_big_img1">; <div id="static_big_img2">...;
  3. если большой картинке надо передать подпись от миниатюры, такую подпись следует указывать в атрибуте картинки title;
  4. если надо передавать более сложные конструкции (форматированный текст, другие элементы), можно добавить договорённости – добавить к элементу галереи дополнительные html-элементы и искать их потом в javascript;
  5. элемент галереи должен быть div с атрибутом class="item";
  6. наименование обработчика всегда начинается с заглавной буквы, а соответствующий идентификатор, генерируемый на сервере, почему-то всегда с маленькой (так сложилось).

Собственно javascript-обработчик

На сервере в коде HTML мы создаём несколько меток (classname, id) в соответствии с договорённостью. Весь программный код галереи изображений пишется на javascript.

Первая очевидная задача – при щелчке по миниатюре вывести на экран браузера соответствующее большое изображение. Файла здесь должно быть минимум два: один, допустим, 160 х 160 пикселов, второй – 1100 х 800 пикселов. Может быть ещё один файл – большего размера. Но ясно, что выводить список миниатюр, используя большие файлы, плохо – потому что страница будет грузиться долго.

Итак, в html есть контейнер, в нём три маленьких картинки:

У картинок, понятно, есть атрибуты class="thumb", alt="путь_к_большой_картинке". Можно в javascript написать просто:

А потом внутри функции showBigImage определять, по какому именно элементу-картинке внутри контейнера был щелчок. Но нам всё равно надо подготавливать для работы картнки-миниатюры, в каждой что-то менять; поэтому мы обходим их в цикле и каждой назначаем обработчик: img.onclick = showBigImage;

Большую картинку надо центрировать на странице внутри специальной области, добавлять стрелки для листания, добавлять подпись или ещё какую-то информацию (например, "микронавигацию" в виде точек или квадратиков под большой картинкой). То есть область с большой картинкой тоже надо готовить, "рисовать" с помощью javascript.

Основа функции showBigImage в нашей системе очень проста:

До этого мы, конечно, уже создали на странице невидимый BigBox с большой картинкой bigImg внутри. А после щелчка по миниатюре делаем этот контейнер видимым. Немедленно возникает вторая часть работы: надо как-то убрать всплывший контейнер с большой картинкой. Обычно для закрытия большого изображения рисуется крестик справа вверху. Ну, и для большего удобства, если пользователь промахнётся, при щелчке по затемнённому фону страницы тоже надо скрывать всё всплывшее.

Из кода видно, что каждой картинке присваивается свойство box (в процессе обхода списка в цикле), в котором содержится ссылка на общий контейнер всех картинок. Ну, а при создании контейнера для большого изображения, мы должны позаботиться о том, чтобы большое изображение в этом контейнере связать с общим контейнером через свойство bigImg.

Это основная рутинная задача любого javascript-обработчика – связать нужные html-элементы страницы посредством ссылок-свойств друг с другом. В прошлом веке для этого было принято городить на сервере кучу уникальных id.

В первом приближении связей тоже можно много не городить – навесить обработчик всё-таки на общий контейнер. В итоге получается очень простой код, посмотреть его можно здесь: imgList.html.

Мы используем синтаксические сокращения, что-то вроде ac == appendChild. Все эти упрощающие функции находятся в файле-библиотеке common.js. С использованием таких сокращений код нашей простой галереи изображений вмещается в 50 строк javascript и примерно столько же CSS (надо ведь как-то располагать миниатюры изображений в ряды и колонны, надо центрировать большую картинку, надо, наконец, нарисовать закрывающий крест...).

Навигация

Сразу видно неудобство "простой" галереи: когда всплывает большая картинка, она заслоняет всё остальное, и мы не можем выбрать другой элемент – надо сначала закрыть большое изображение. А когда мы его закрываем, мы не видим в списке, по какой миниатюре щёлкнули.

Будем отмечать выбранную миниатюру стилем curr. Это легко – добавить класс при щелчке по элементу, но при щелчке по следующей миниатюре у предыдущей щёлкнутой надо класс убирать обратно. Для этого заведём у общего контейнера каталога свойство "текущий элемент" – currThumb, чтобы оперировать этим элементом при вызове следующего большого изображения.

На самом деле нам понадобится не сама миниатюра, а контейнер, в которой она находится – div. Его надо находить и для старой и для новой миниатюры. Количество возни возрастает. Поэтому уже становится проще обойти сначала все элементы-миниатюры в цикле и назначить все необходимые свойства – чтобы потом легко к ним обращаться: thumb.itemBox, например.

Микронавигация

Чтоб два раза не бегать, во время обхода миниатюр заодно создадим их "индекс" – box.thList, в который поместим по очереди ссылки на все миниатюры. Тогда, зная текущую, мы можем вычислить следующую и предыдущую миниатюры и создать на них ссылки рядом с большим изображением.

Мы пока не будем делать стрелки, так как они менее информативны, рассчитаны на тупое листание и просмотр "хоть чего-нибудь". Более мощный инструмент – микронавигация. Обычно её делают в виде квадратиков или точек под большим изображением. Но в таком варианте мы тоже видим мало – только как далеко мы продвинулись от начала списка.

Почему бы не заполнить эти квадратики собственно изображениями? Для этого не надо создавать ещё один набор "совсем-уменьшенных" картинок, мы можем использовать файлы миниатюр – они ведь уже загружены в браузер и второй раз скачиваться с сервера не будут.

Присоединим (в функции подготовки imgList.init()) к области большого изображения контейнер:

А затем во время того же самого обхода миниатюр будем добавлять в микро-контейнер ма-аленькое изображение, привязанное к каждой миниатюре. Ма-аленьким его будет делать, конечно же CSS.

Мы складываем микро-картинки не сразу в контейнер micro, а во вложенный в него Band – ленту, так как предполагаем, что ширина этой ленты будет больше контейнера и мы будем потом эту ленту прокручивать по горизонтали, чтобы увидеть скрытые микро-ссылки.

Стало намного лучше: index2.html – код вырос до 80 строк (было 50). Но если учесть, что изображённое в элементе каталога пользователь чаще всего хочет купить, наша галерея нефункциональна. Как пользователь сможет описать выбранное?

Подписи и стрелки

Ясно, что под большим изображением надо выводить подпись миниатюры. Для этого добавим три строчки: 1) элемент div в "большой контейнер", 2) свойство descr для миниатюры, которое свяжем с элементом-подписью, 3) при показе большого изображения будем передавать информацию от миниатюры в элемент подписи в большом контейнере.

Теперь стало "функционально". Заодно добавим небольшое удобство – передвижение по микронавигации справа налево с помощью стрелок на клавиатуре. Для этого придётся использовать событие document.onkeydown.

Заодно подкорректируем микро-ленту: если текущий элемент не видно (он сдвинут правее или левее), мы будем передвигать ленту до достижения видимости элемента. Куча измерений, всяких расстояний, это скучно. Но результат есть, и можно перебирать изображения с помощью стрелочек на клавиатуре: index3.html – код вырос ещё на 20 строк (стал 105).

Теперь возникает вопрос: а зачем нам вообще нарисованные стрелки для листания? Мы ведь можем сразу выбрать мышкой нужный элемент в микронавигаторе. Ну, во-первых, для соблюдения традиции. Это ведь мы с вами тут такие продвинутые, а трусливые веб-мастера на вопрос клиента "где стрелки?" только потупят взгляд и побегут исполнять (и нас материть). Приятно, конечно, потроллить веб-мастеров с их клиентами. Но истина дороже: нарисованные стрелки позволят нам поместить дополнительную информацию об "окружении" текущего элемента – ту же подпись или краткое описание.

И, раз уже речь зашла о текстовой информации (о подписях), то хорошо бы это как-то и в микронавигаторе отобразить. Целые подписи не влезут, конечно (навигатор перестанет быть "микро"). Но можно ведь сделать всплывающие – с помощью обычного html-атрибута title.

Теперь каталог готов: index4.html – код получился 130 строк, и CSS 60 строк.

Комментарии