Объект Image
Наиболее зрелищные эффекты при
программировании на JavaScript достигаются при работе с
графикой. При этом в арсенале программиста не так уж много
инструментов: встроенные в документ картинки, возможность генерации
объекта Image, комбинирование картинок с гипертекстовыми ссылками и
таблицами. Тем не менее обилие различных эффектов, которые
достигаются этими нехитрыми средствами, впечатляет.
Программирование графики в JavaScript опирается на
объект Image, который характеризуется следующими свойствами,
методами и событиями:
Свойства |
Методы |
События |
|
нет |
|
Несмотря на такое обилие свойств, их абсолютное большинство можно
только читать, но не изменять. Об этом свидетельствует, прежде
всего, отсутствие методов. Но два свойства все же можно изменять:
src и lowsrc. Этого оказывается достаточно для множества эффектов с
картинками.
Все объекты класса Image можно разделить на
встроенные и порожденные программистом. Встроенные объекты — это
картинки контейнеров IMG. Если эти картинки поименовать, к
ним можно обращаться по имени:
<A HREF="javascript:void(0);"
onClick="window.alert('Image
name:'+
document.images[0].name)">
<IMG NAME=intuit
SRC=images.gif BORDER=0>
</A>
Картинка активна.
Если на нее нажать, получим имя контейнера IMG. Обращение
document.images[0].name позволяет распечатать это имя в окне
предупреждения. При этом само имя указано как name=intuit в
контейнере IMG.
К встроенному графическому
объекту можно обратиться и по индексу:
document.images[0];
В данном случае images[0] — это
первая картинка документа.
src и lowsrc
Свойства src и lowsrc определяют URL изображения,
которое монтируется внутрь документа. При этом lowsrc определяет
временное изображение, обычно маленькое, которое отображается, пока
загружается основное изображение, чей URL указывается в атрибуте SRC
контейнера IMG. Свойство src принимает значение атрибута
SRC контейнера IMG. Программист может изменять значения и
src, и lowsrc. Рассмотрим пример с src:
document.i2.src="images2.gif";
Как видно из этого
примера, существует возможность модифицировать вмонтированную
картинку за счет изменения значения свойства src встроенного объекта
Image. Если вы в первый раз просматриваете данную страницу (т.е.
картинки не закешированы браузером), то постепенное изменение
картинки будет заметно. Как ускорить это изменение, мы рассмотрим в
следующем разделе.
Изменение картинки
Изменить картинку можно, только присвоив свойству src
встроенного объекта Image новое значение. На странице
"Программирование графики" показано, как это делается в простейшем
случае. Очевидно, что медленная перезагрузка картинки с сервера не
позволяет реализовать быстрое листание. Попробуем решить эту
проблему.
Собственно, решение заключается в разведении по
времени подкачки картинки и ее отображения. Для этой цели
используют конструктор объекта Image:
<TABLE>
<TD>
<A
HREF="javascript:void(0)";
onMouseover="document.m0.src=color[0].src;
return
true;"
onMouseout="document.m0.src=mono[0].src;
return
true;">
<IMG NAME=m0 SRC="images0.gif"
border=0>
</A>
</TD>
...
</TABLE>
Фрагмент кода показывает типовой прием замещения
и восстановления картинки при проходе курсора мыши. Естественно, что
менять можно не одну, а сразу несколько картинок.
Главное, тем
не менее, не в том, что картинки замещаются, а в том, с какой
скоростью они это делают. Для достижения нужного результата в начале
страницы создаются массивы картинок, в которые перед отображением
перекачивается графика (обратите внимание на строку статуса
при загрузке страницы):
color = new Array(32);
mono = new
Array(32);
for(i=0;i<32;i++)
{ mono[i] = new
Image();
color[i] = new
Image();
if(i.toString().length==2)
{
mono[i].src =
"images0"+i+".gif";
color[i].src =
"images0"+i+".gif";
}
else
{
mono[i].src =
"images0"+i+".gif";
color[i].src = "images0"+i+".gif";
}
}
Еще один характерный прием — применение функции отложенного
исполнения JavaScript-кода (eval()):
function
def()
{
for(i=0;i<32;i++)
{
eval("document.m"+i+".src=mono["+i+"].src");
}
for(i=0;i<5;i++)
{
eval("document.r"+i+".src=rm["+i+"].src");
}
}
В данном случае eval() избавляет нас от необходимости
набирать операции присваивания (32 строки — это не фунт изюму).
Мультипликация
Естественным продолжением идеи
замещения значения атрибута SRC в контейнере IMG является
мультипликация, т.е. последовательное изменение значения этого атрибута во
времени. Для реализации мультипликации используют метод объекта Window —
setTimeout().
Собственно, существует два способа запуска
мультипликации:
Наиболее популярный — setTimeout() при onLoad().
Событие
onLoad()
Событие onLoad() наступает в момент окончания
загрузки документа браузером. Обработчик события указывается в контейнере
BODY:
...
<BODY onLoad="JavaScript_code">
...
В
нашем случае при загрузке документа должен начать выполняться цикл
изменения картинки:
function
movie()
{
eval("document.images[0].src='clock"+
i+".gif';");
i++;if(i>6)
i=0;
setTimeout("movie();",500);
}
...
<BODY
onLoad="movie();">
...
В примере используется бесконечный
цикл, хотя можно реализовать и конечное число подмен:
function
movie()
{
eval("document.images[0].src='clock"+
i+".gif';");
i++;
if(i<7)
{
setTimeout("movie();",500);}
}
...
<BODY
onLoad="movie();">
В обоих примерах следует обратить внимание на
использование метода setTimeout(). На первый взгляд, это просто рекурсия.
Но в действительности все несколько сложнее. JavaScript разрабатывался для
многопоточных операционных систем, поэтому правильнее будет представлять
себе исполнение скриптов следующим образом:
После окончания срока задержки исполнения все повторяется. В первом
примере (бесконечное повторение) функция порождает саму себя и, тем самым,
поддерживает непрерывность своего выполнения. Во втором примере (конечное
число итераций) после девяти повторов функция не порождается. Это приводит
к завершению процесса отображения новых картинок.
Запуск и
остановка мультипликации
Перманентная мультипликация может
быть достигнута и другими средствами, например многокадровыми
графическими файлами. Однако движение на странице — не всегда
благо. Часто возникает желание реализовать запуск и останов движения по
требованию пользователя. Удовлетворим это желание, используя предыдущие
примеры (запустить или остановить мультипликацию):
var
flag1=0;
function
movie()
{
if(flag1==0)
{
eval("document.images[0].src='clock"+
i+".gif';");
i++;if(i>6)
i=0;
}
setTimeout("movie();",500);
}
...
<BODY
onLoad="movie();">
...
<FORM>
<INPUT TYPE=button
VALUE="Start/Stop"
onClick="if(flag1==0) flag1=1; else
flag1=0;">
</FORM>
В данном случае мы просто обходим
изменение картинки, но при этом не прекращаем порождение потока. Если мы
поместим setTimeout() внутрь конструкции if(), то после нажатия на кнопку
Start/Stop поток порождаться не будет, и запустить движение будет нельзя.
Существует еще один способ решения проблемы остановки и старта
мультипликации. Он основан на применении метода clearTimeout(). Внешне все
выглядит по-прежнему, но процесс идет совсем по-другому:
var
flag1=0;
var id1;
function
movie()
{
eval("document.images[0].src='clock"+
i+".gif';");
i++;if(i>6)
i=0;
id1 = setTimeout("movie();",500);
}
...
<BODY
onLoad="movie();">
...
<FORM>
<INPUT TYPE=button
VALUE="Start/Stop"
onClick="if(flag1==0)
{
id1=setTimeout('movie();',500); flag1=1;}
else {clearTimeout(id1);
flag1=0;};">
</FORM>
Обратите внимание на два
изменения. Во-первых, объявлен и используется идентификатор потока (id1);
во-вторых, применяется метод clearTimeout(), которому, собственно,
идентификатор потока и передается в качестве аргумента. Чтобы остановить
воспроизведение функции movie() достаточно "убить" поток.
Оптимизация отображения
При программировании
графики следует учитывать множество факторов, которые влияют на
скорость отображения страницы и скорость изменения графических
образов. При этом обычная дилемма оптимизации программ — скорость или
размер занимаемой памяти — решается только путем увеличения скорости. О
размере памяти при программировании на JavaScript думать как-то не
принято.
Из всех способов оптимизации отображения картинок мы
остановимся только на нескольких:
Если первые две позиции относятся в равной степени как к отображению
статических картинок, так и к мультипликации, то третий пункт характерен
главным образом для мультипликации.
Оптимизация при загрузке
Практически в любом руководстве по разработке HTML-страниц
отмечается, что при использовании контейнера IMG в теле
HTML-страницы следует указывать атрибуты WIDTH и HEIGHT. Это продиктовано
порядком загрузки компонентов страницы с сервера и алгоритмом работы
HTML-парсера. Первым загружается текст разметки. После этого парсер
разбирает текст и начинает загрузку дополнительных компонентов, в том
числе графики. При этом загрузка картинок, в зависимости
от типа HTTP-протокола, может идти последовательно или параллельно.
Также параллельно с загрузкой парсер продолжает свою работу. Если для
картинок заданы параметры ширины и высоты, то можно отформатировать текст
и отобразить его в окне браузера. До тех пор, пока эти параметры не
определены, отображения текста не происходит.
Таким образом указание
высоты и ширины картинки позволит отобразить документ раньше, чем картинки
будут получены с сервера. Это дает пользователю возможность читать
документ или задействовать его гипертекстовые ссылки до момента полной
загрузки (событие load).
С точки зрения JavaScript, указание размеров
картинки задает начальные параметры окна отображения графики
внутри документа. Это позволяет воспользоваться маленьким прозрачным
образом, для того, чтобы заменить его полноценной картинкой. Идея состоит
в передаче маленького объекта для замещения его по требованию большим
объектом.
Предварительная загрузка
Замена одного
образа другим часто бывает оправдана только в том случае, когда это
происходит достаточно быстро. Если перезагрузка длится долго, то эффект
теряется. Для быстрой подмены используют возможность предварительной
загрузки документа в специально созданный объект класса Image.
Реальный эффект можно почувствовать только при отключении кэширования
страниц на стороне клиента (браузера). Кэширование часто используют для
ускорения работы со страницами Web-узла. Как правило, загрузка первой
страницы — это достаточно длительный процесс. Самое главное, чтобы
пользователь в этот момент был готов немного подождать. Поэтому, кроме
графики, необходимой только на первой странице, ему можно
передать и графику, которая на ней не отображается. Но зато при
переходе к другим страницам узла она будет отображаться без задержки на
передачу с сервера.
Описанный выше прием неоднозначен. Его оправдывает
только то, что если пользователь нетерпелив, то он вообще отключит
передачу графики.
Нарезка картинок применяется довольно часто. Она позволяет
достигать эффекта частичного изменения отображаемой картинки. Чаще всего
он применяется при создании меню.
Кроме подобного эффекта нарезка
позволяет реализовать мультипликацию на больших картинках. При этом
изменяется не весь образ, а только отдельные его части.
Одним из наиболее популярных приемов дизайна страниц Web-узла является техника нарезки картинок на составные части. Можно выделить следующие способы применения этой техники для организации навигационных компонентов страницы:
Главной проблемой при использовании нарезанной графики является защита ее от контекстного форматирования страницы HTML-парсером. Дело в том, что он автоматически переносит элементы разметки на новую строку, если они не помещаются в одной. Составные части нарезанной картинки должны быть расположены определенным образом, поэтому простое их перечисление в ряд не дает желаемого эффекта:
<IMG SRC="image1.gif"><IMG
SRC="image2.gif"><IMG
SRC="image3.gif"><IMG
SRC="image4.gif">
Элементы переносятся на новую строку, так как ширина раздела меньше общей ширины всех картинок. Проблема решается, если применить защиту от парсера — <PRE>:
<PRE>
<IMG SRC="image1.gif"><IMG
SRC="image2.gif"><IMG
SRC="image3.gif"><IMG
SRC="image4.gif"> </PRE>
Использование такого меню требует определения на нем гипертекстовых ссылок, что приводит к следующему эффекту:
<PRE> <A HREF="javascript:void(0);"><IMG
SRC="image1.gif"></A><A
HREF="javascript:void(0);"><IMG
SRC="image2.gif"></A><A
HREF="javascript:void(0);"><IMG
SRC="image3.gif"></A><A
HREF="javascript:void(0);"><IMG
SRC="image4.gif"></A> </PRE>
Этого можно достичь за счет применения атрибута BORDER равного 0:
<PRE>
<A HREF="javascript:void(0);"><IMG
SRC="image1.gif" BORDER="0"></A><A
HREF="javascript:void(0);"><IMG
SRC="image2.gif" BORDER="0"></A><A
HREF="javascript:void(0);"><IMG
SRC="image3.gif" BORDER="0"></A><A
HREF="javascript:void(0);"><IMG
SRC="image4.gif" BORDER="0"></A> </PRE>
Теперь попробуем
тем же способом реализовать многострочное меню:
Сплошной
картинки не получается, так как высота строки не равна высоте
картинки. Подогнать эти параметры практически невозможно. Каждый
пользователь настраивает браузер по своему вкусу. Решение
заключается в использовании таблицы:
В данном случае все картинки
удается сшить без пропусков и тем самым достичь непрерывности
навигационного дерева. Пропуски устраняются путем применения
атрибутов BORDER, CELLSPACING и CELLPADDING. Первый устраняет
границы между ячейками, второй устанавливает расстояние между
ячейками равным 0 пикселов, третий устанавливает отступ между
границей ячейки и элементом, помещенным в нее, в 0 пикселов.
В данном
разделе мы не будем рассматривать обработчики событий контейнера
IMG. Мы остановимся на наиболее типичном способе
комбинирования обработчиков событий и изменения графических
образов. Собственно, не имело бы смысла применять нарезанную
графику, если бы не возможность использования обработчиков
событий для изменения отдельных частей изображения. Продолжая
обсуждение примера с навигационным деревом, покажем его развитие с
обработкой событий, вызванных наведением мыши на объект, и
изменением картинок:
В данном примере при проходе курсор мышки
через картинки меню последние изменяются. Этот эффект достигается за
счет применения двух событий: onMouseover и onMouseout. По первому
событию картинка меняется с позитива на негатив, по второму событию
восстанавливается первоначальный вариант. Следует заметить, что
события определены в контейнере якоря (A), а не в контейнере
IMG. Это наиболее устойчивый с точки зрения совместимости
браузеров вариант.
Практически все, что
изложено в разделах "Графика и таблицы" и "Графика и обработка
событий" касается вопросов построения одноуровневых меню. Поэтому в
данном разделе мы постараемся привести более или менее реальные
примеры таких меню. Графическое меню удобно тем, что автор может
всегда достаточно точно расположить его компоненты на экране. Это, в
свою очередь, позволяет и другие элементы страницы точнее
располагать относительно элементов меню:
В данном случае
стрелочка бежит точно над тем элементом, на который указывает мышь.
По большому счету, применение атрибута ALT у IMG и его дублирование
в строке статуса является гораздо более информативным, чем
добавление нового графического элемента. Правда, отображается
содержание ALT с некоторой задержкой:
Посмотрим теперь на
реализацию вертикального меню, построенного на основе графических
блоков текста, как сейчас это принято делать:
При движении мыши
у соответствующего компонента, попавшего в фокус мыши, "отгибается
уголок". В данном случае "уголок" — это самостоятельная картинка.
Все уголки реализованы в правой колонке таблицы. Для того чтобы
гипертекстовая ссылка срабатывала по обеим картинкам (тексту и
"уголку"), применяются одинаковые контейнеры A, охватывающие
графические образы. В этом решении есть один недостаток: при
переходе от текста к "уголку" последний "подмигивает". Картинки
можно разместить и в одной ячейке таблицы, но тогда нужно задать ее
ширину, иначе при изменении размеров окна браузера картинки могут
"съехать". Чтобы убрать "подмигивание", необходимо сделать
полноценные картинки замены.
"Подмигивание" происходит при
переходе с одного элемента разметки контейнера на другой. При этом
заново определяются свойства отображения элемента.
Вложенные
меню
При обсуждении программирования форм отмечено, что в HTML
нет стандартного способа реализации вложенных меню. Тем не менее за
счет графики можно создать их подобие. При этом следует понимать,
что место, на которое ложится графика, нельзя заполнить текстом:
В этом примере вложенное меню расположено справа от основного.
Эффект вложенности достигается за счет изменения цвета.
Подчиненность меню можно подчеркнуть изменением его положения
относительно основного меню: (
В этом случае для продвижения
меню вниз необходимо зарезервировать место при помощи невидимых или
видимых картинок. Это не обязательно должны быть иллюстративные
картинки, которые не несут никакой нагрузки.
При использовании
слоев можно создать настоящее выпадающее меню.
Дело в том, что в его постановке и выводах произведена подмена, аналогичная подмене в школьной шуточной задачке на сообразительность, в которой спрашивается:
- Cколько яблок на березе, если на одной ветке их 5, на другой ветке - 10 и так далее
При этом внимание учеников намеренно отвлекается от того основополагающего факта, что на березе яблоки не растут, в принципе.
В эксперименте Майкельсона ставится вопрос о движении эфира относительно покоящегося в лабораторной системе интерферометра. Однако, если мы ищем эфир, как базовую материю, из которой состоит всё вещество интерферометра, лаборатории, да и Земли в целом, то, естественно, эфир тоже будет неподвижен, так как земное вещество есть всего навсего определенным образом структурированный эфир, и никак не может двигаться относительно самого себя.
Удивительно, что этот цирковой трюк овладел на 120 лет умами физиков на полном серьезе, хотя его прототипы есть в сказках-небылицах всех народов всех времен, включая барона Мюнхаузена, вытащившего себя за волосы из болота, и призванных показать детям возможные жульничества и тем защитить их во взрослой жизни. Подробнее читайте в FAQ по эфирной физике.