Javascript.RU

Фотогаллерея на JS

Часть 0. Преамбула.

Начиная с этого момента, я буду стараться регулрно писать статьи о прикладном JS. Я не буду рассуждать о преимуществах JS или его истории, я просто буду брать реальные задачи и рассказывать их решение. По мимо самого рассказа и кода у меня будут вставки, например:

---Совет-----------------------------------------------------------------------------
Сдесь будет даваться совет.
--------------------------------------------------------------------------------------

или

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

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

Часть 1. Постановка и соображения на счёт PHP и JS.

Многие наверное знают написанные на PHP галлереи, которые могут легко прокручиваться на манер плёнки фотоапарата, а также крайне просты в использовании. Сейчас у нас случай не обычный, перед нами поставили задачу написать JS галерею по образцу "Уцоза". Не знаю у кого-как, но в нашем регионе(Донецкая обл.) есть существенные проблемы с хостингом, и за более-менее нормальную цену можно договориться только на HTML/CSS/JS. Да и к тому же многим сайтом PHP не нужен. Так что задача - жизненна. Есть ещё одно преимушество: если пишешь довольно крупный сайт и на него будет заходить много людей, а сервер будет слабенький(всё это очень возможно), то при использовании для написания всех модулей языка PHP, можно положить сервер. Поэтому я считаю, что если что-то можно сделать посредством клиента, то это именно так и нужно делать. А разработка будет у нас проходить в три этапа: 1. Общее проэктирование. 2. Создание дизайна. 3. Разработка движка. Поскольку задача не очень сложна, то постановка не составит труда. Главное разобраться: какой хотел бы видеть галлерею заказчик? Увы, это практически не возможно сделать, и прийдёться на моменте разработки дизайна постоянно показывать своё ваяние заказчику и слушать от него упрёки. Эх, нелегка жизнь программиста... Потом мы должны будем написать пару функций и всё это соединить в одну конструкцию.

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

Есть несколько требований к самим функциям: 1. Их можно вызывать из любой точки сайта. 2. Они должны быть максимально универсальными для всех дизайнов.
Вроде бы мы определились с тем что мы хотим получить. И можем всерьёз задуматься над тем что для этого нужно.

Часть 2. Предсатвление данных.

Я предлагал бы все библиотеки хранить в папке jslib корневой дерриктории сайта. Тогда у нас не будет дубликатов и будет простая ссылка вида myhost.ua/jslib/gallery.js. А галереи для каждой странички хранить в отдельной папке.
---Совет-----------------------------------------------------------------------------
Храните страницы в отдельных деррикториях. Например у нас есть сайт о футболе. Тогда список футбольных турниров текущего года будет храниться в football.fbc/tourn[нынешний_год]/body.html. А страничка турнира будет храниться в football.fbc/tourn[нынешний_год]/[название_турнира]/body.html. Их фотографии в football.fbc/tourn[нынешний_год]/[название_турнира]/gallery/. Благодаря этому у нас получиться ссылка на все фото типа gallery/ И нам не нужно будет каждый раз функции указывать папку из которой будут грухиться фото. Иначе у вас будет каша и через небольшой промежуток времени, вы сами не будете знать что и где храниться. При написании сайтов чаще всего используют именно такую структуру.
--------------------------------------------------------------------------------------
Но, есть большая проблема, средствами JS невозможно вернуть список файлов дерриктории. И нужно использовать шаблон. Например: img_[номер_фотографии_от_000_до_999]. Благо есть мощнейшие программы для сортировки галерей, которые позволяют это легко сделать, да и к тому же, когда фотограф сделал снимок, он его не сжимает, а отдаёт вам с весом около 2 мегобайт, а при сжатии фотографии(надеюсь, все согласяться, что сжимать фотографии нужно) можно потратить 2 секунды и написать правильное имя файла. Определясь, каким образом мы будем хранить данные мы можем приступить к созданию интерфейса.

Часть 3. Дизайн.

Для начала инициализируем библиотеку и запустим функцию построения массива(о ней, будет написано в четвёртой части)

<script type="text/javascript" src="football.fbc/jslib/gallery.js">
</script>
<body onload="gallery(24)">
</body>

Функция gallery(x) строит массив изображений по пути относительно документа из которого она запускаеться: gallery/ х - это кол-во фотографий в дерриктории.(вы можете использовать лубые другие имена, так же вы можете использовать другой дизайн, индетификаторы объектов и т.д.)
Теперь строим таблицу с тремя фотографиями:

<table align="center">
<tr><td><img src="" name="left" height="190" width="260" onClick="backImg()"></td><td><img src="" name="main"></td><td><img src="" name="right" height="190" width="260" onClick="nextImg()"></td></tr>
</table>

Я советую брать одновременно только три фотографии, хотя можно и 5, и 7, но тогда будет уменьшаться скорость загрузки, да и места на странице будет заниматься уйма. src мы оставляем пустым, поскольку не знаем что будет туда загружаться, а о функциях, я опять-таки не распространяюсь, сейчас мы строим интерфейс, а не разбираем JS библиотеку. Следующий этап - построение навигационного меню, для того что бы можно было переходить к следующе и предыдущей фотографии, а также к началу и концу(я считаю что можно оставить только возврат к началу, ибо конец это начало с перемоткой на одну фотографию).

<p align="center">
<a onClick="begin()"><img src="../../img/button/p_gal3.gif"  onMouseOver="mimg=this.src; this.src='../../img/button/a_gal3.gif';" onMouseOut="this.src=mimg;"></a>
<a onClick="backImg()"><img src="../../img/button/p_gal1.gif"  onMouseOver="mimg=this.src; this.src='../../img/button/a_gal1.gif';" onMouseOut="this.src=mimg;"></a>
<a onClick="nextImg()"><img src="../../img/button/p_gal2.gif"  onMouseOver="mimg=this.src; this.src='../../img/button/a_gal2.gif';" onMouseOut="this.src=mimg;"></a>
<a onClick="end()"><img src="../../img/button/p_gal4.gif"  onMouseOver="mimg=this.src; this.src='../../img/button/a_gal4.gif';" onMouseOut="this.src=mimg;"></a></p>

Хочу заметить приём работы с изменяемыми кнопками: когда мы наводим на них, ссылка на изображение сохраняеться в переменной и подставляеться другая ссылка, когда мы убираем курсор src присваиваеться переменная. Только надо быть осторожным, и не забывать писать this.src, а не src. Перед меню можно добавить строку статуса(т.е. общее кол-во фотографий и номер текущей фотографии):

<form>
<p align="center">
<input type="text" value=" " id="data" size="30" readonly />
</p>
</form>

Советую присвоить readonly что бы пользователь случайно не ткнул на поле. Естественно все фрагменты надо умещать до

Теперь когда мы разобрались с дизайном можно приступить к моей любимой части - написанию скрипта.

Часть 4. Написание скрипта.

Для начала нам нужна функция, которая бы заполняла массив фотографиями

var imgArray = new Array() //Создаём пустой массив
function gallery(i) { //Функция заполнения массива данными
	var a = 0
	while (a <= i) {
		if ( a < 10) { //Если номер фотографии < 10
			imgArray[a - 1] = new Image()
			imgArray[a - 1].src = "gallery/img_0" + a + ".jpg"//Путь к фото
		} else {
			imgArray[a - 1] = new Image()
			imgArray[a - 1].src = "gallery/img_" + a + ".jpg"
		}
	a += 1
	}
	begin()// Функция начального отображения
}

Большинство программ для работы с галереями, при сортировки изображений используют в именнах занчащие нули т.е. 001, а не 1. Поэтому и идёт проверка на разную степень десятки.(если нужно работать с трёхзначными номерами фотографий, то нужно изменить скрипт так что бы он приобрёл следующий вид:

var imgArray = new Array() //Создаём пустой массив
function gallery(i) { //Функция заполнения массива данными
	var a = 0
	while (a <= i) {
		if ( a < 10) { //Если номер фотографии < 10
			imgArray[a - 1] = new Image()
			imgArray[a - 1].src = "gallery/img_00" + a + ".jpg"
		} else if ( a < 100) { //Есди номер фотографии от 10 до 100
                                  imgArray[a - 1] = new Image()
			imgArray[a - 1].src = "gallery/img_0" + a + ".jpg"
                      } else {
			imgArray[a - 1] = new Image()
			imgArray[a - 1].src = "gallery/img_" + a + ".jpg"
		}
	a += 1
	}
	begin()// Функция начального отображения
}

ну и так далее, для работы с более крупной степенью) Данную функцию можно сделать так, что бы она работала не зависимо от кол-ва фотографий(но при условии того, что если какого-то номера не будет, то последующие номера не будут отрабатываться). Просто надо добавить блок проверки на существующие изображение:

document.main.src = "gallery/img_" + a + ".jpg"
if (document.main.height != 0) {//Если изображение существует - загружаем 
   imgArray[a - 1] = new Image()
   imgArray[a - 1].src = "gallery/img_" + a + ".jpg"
} else {//Если нет - то считаем, что изображений больше нет 
   break;
}

Но скорость работы приемлема только на локальном компьютере в котором есть фотографии. Иначе загрузка страницы может затянуться на пол-минуты, взависимости от кол-ва изображений, поэтому делать это не советую, хотя и можно, да и практика показало что это не так уж и страшно.(но на некоторых серверах возможны проблемы)
---Баранов спрашивает---------------------------------------------------------------Если мы можем проверить существует данная фотография или нет, то почему мы не можем сразу работать с файлами, которые, допустим, были сняты на фотоапарате от Canon?(имя файла там следующее: DSC_[некое_большое_число]
Ответ: файла там не упорядоченны, первая фотография может называться - DSC_0909738, а вторая - DSC_8709078. Т.е. нужно перебирать все цифры, что занимает очень много времени, а главное может выдавать самые необычныерезультаты, к примеру там где лежат эти две фотографии, скрипт найдёт тысяч 5, и будет прокручивать одну и ту же фотографию.
--------------------------------------------------------------------------------------
Далее мы создадим функции управления. Только перед этим нужно усвоить, что первая строка массива это 0, а последняя array.length - 1.

function begin() {//Начальная позиция
	document.main.src  = imgArray[0].src//Первая фотография
	document.left.src  = imgArray[imgArray.length - 1].src//Последняя
	document.right.src = imgArray[1].src
	createData()//Возвращаем строку статуса
}
function end() {//Конечная позиция
	document.main.src  = imgArray[imgArray.length - 1].src//Последняя
	document.left.src  = imgArray[imgArray.length - 2].src
	document.right.src = imgArray[0].src//Первая фотография
	createData()
}
function backImg () {//Перемотка с лева на право в один шаг
	var mainImg  = document.main.src//Сохраняем позицию основного фото
	var leftImg  = document.left.src//Сохраняем позицию левого фото
	for (var i = 0; i < imgArray.length; i++) {//Ищем основное фото в массиве
		if (mainImg == imgArray[i].src) {
			if (leftImg == imgArray[0].src) {/*Если левое изображение
- начало массива, то меняем его на конец массива*/
				document.left.src = imgArray[imgArray.length - 1].src
			} else if (leftImg == imgArray[imgArray.length - 1].src) {
/*Если левое фото конец массива, то перемещаемя на -1 от конца*/
				document.left.src = imgArray[imgArray.length - 2].src
			} else {/*Иначе новая позиция левого изображения всегда на -2 меньше старой позиции основного фото*/
				document.left.src = imgArray[i - 2].src
			}
//Меняем остальные изображения
			document.main.src = leftImg
			document.right.src = mainImg
			mainImg  = document.main.src
			leftImg  = document.left.src
			break
		}
	}
	createData()//Выводим новый статус
}
function nextImg() {//Перемотка с права на лево на один шаг
//Сохраняем позиции фотографий
	var mainImg  = document.main.src
	var rightImg = document.right.src
	for (var i = 0; i < imgArray.length; i++) {
		if (mainImg == imgArray[i].src) {
/*Если правое фото конец массива, то присваиваем ему начало массива*/
			if (rightImg == imgArray[imgArray.length - 1].src) {
				document.right.src = imgArray[0].src
/*Если - начало, то начало + 1*/
			} else if (rightImg == imgArray[0].src) {
				document.right.src = imgArray[1].src
/*Иначе новая позиция правого изображения всегда на 2 больше, старой позиции основного*/ 
			} else {
				document.right.src = imgArray[i + 2].src
			}
//Меняем остальные изображения
			document.main.src = rightImg
			document.left.src = mainImg
			mainImg  = document.main.src
			rightImg = document.right.src
			break
		}
	}
	createData()//Вывводим статус
}

Ухххххх! Основную часть сделали, надеюсь тех коментраиев, которые встречаються в коде будет достаточно. Если всё же что-то непонятно, возьмите фотоплёнку и покрутите её влево и вправо, а потом напишите алгоритм на бумажке. Осталась последняя часть - статус.

function createData() {//Функция выводящая статус
	var a = "Фотография №: "//Строка с шапкой
	for (var i = 0; i < imgArray.length; i++) {//Ищем главное фото
		if (document.main.src == imgArray[i].src) {//Если нашли то пишем№
			a += i + 1
			break;
			alert(i)
		}
	}
	a += "; " + "Всего: " + imgArray.length//Сколько всего?
	document.getElementById("data").value = a//Выводим
}

Ну вот и всё, скрипт готов к работе.

P.S. Если вам понравилось и вы желаете почитать что-нибудь ещё в этом роде - оставляйте комментарии, с описанием того скрипта который вы бы хотели увидеть. Да прибудет с нами... JS 2.0! Несколько позже, дам ссылку на готовое.(хотя здесь всё есть, только листинг по кускам надо собрать)

+1

Автор: Гость (не зарегистрирован), дата: 23 июля, 2009 - 22:56
#permalink

Блин, купите себе орфографический словарь, а то это же просто читать невозможно! =(


Автор: Гость (не зарегистрирован), дата: 23 августа, 2009 - 14:01
#permalink

Спс. Оч полезная статья


Автор: Гость (не зарегистрирован), дата: 19 ноября, 2009 - 09:14
#permalink

хочу ссылку на готовое!
было бы здорово если вы опишите скрипт галереи где много маленьких фоток,которые прокручиваются стрелками вправо - влево,а при нажатии появляется большое фото в блоке выше.


Автор: Гость (не зарегистрирован), дата: 29 января, 2010 - 21:45
#permalink

Спасибо! Ваша статья очень помогла быстро разобраться в основах js!


Автор: Malanya (не зарегистрирован), дата: 19 апреля, 2010 - 11:26
#permalink

понятнее не бывает, рекомендую


Автор: st.solovey, дата: 11 июня, 2010 - 21:31
#permalink

Круто! Я ничего не понял, но буду разбираться. Жаль, что Вы не стали больше ни чего писать.


Автор: Ан (не зарегистрирован), дата: 2 сентября, 2010 - 12:25
#permalink

спасибо большое, полезная статейка)


Автор: Morph (не зарегистрирован), дата: 9 января, 2011 - 01:09
#permalink

Баранов спрашивает =))) :
а почему когда элементами массива назначается картинка

imgArray[a - 1].src = "/forum/gallery/img_00" + a + ".jpg"

я так понимаю из значения а вычитается 1 и выходит массив начинается с элемента "-1" т. к. начальное значение var а=0

я наверно, что то не понимаю ?


Автор: Morph (не зарегистрирован), дата: 9 января, 2011 - 02:33
#permalink

еще

после построения массива из картинок есть строка:

17 begin()// Функция начального отображения

функция должна выводить первую картинку массива.
но эта функция у меня не запускается и картинка не выводиться, хотя массив создан.
однако если добавить:
alert (a)
15 a += 1
16 }
17 begin()// Функция начального отображения
то функция begin() работает о_____О , но каждый раз выскакивает значение а

Все дело в "Опере" или в самом JS?

объясните нубу =)


Автор: Affidebem (не зарегистрирован), дата: 17 января, 2011 - 03:58
#permalink

Поздравляю вас Старо-Новым годом, желаю вам в новом году успехов и спасибо что вы находите время поддерживать ваш замечательный блог!


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
10 + 1 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
Больше записей нет. Прокомментируйте эту запись - может быть, тогда он что-нибудь еще хорошее напишет ;)
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние комментарии
Последние темы на форуме
Forum