Слайд-шоу на Ajax с DHTML и XML

Создание динамических HTML-эффектов с использованием Ajax

Узнайте, как создать слайд-шоу на стороне клиента с Ajax (Asynchronous JavaScript and XML), анимированное при помощи Ken Burns Effects. Вы увидите, как создать исходные XML-данные для Ajax, запросить XML-данные с клиентского компьютера и динамически создать и анимировать HTML-элементы с этим XML.

Джек Херингтон, старший инженер-программист, Code Generation Network

Джек Херингтон (Jack D. Herrington) является старшим инженером-программистом с более чем 20-летним опытом. Он - автор трех книг: "Code Generation in Action", "Podcasting Hacks" и готовящейся к выпуску "PHP Hacks". Написал более 30 статей. Вы можете связаться с ним по адресу jack_d_herrington@codegeneration.net.



18.04.2006

Если бы революция Web 2.0 имела лозунг из одного слова, то это было бы слово Ajax (Asynchronous JavaScript and XML). Интерактивность на стороне клиента в таких приложениях как географическая служба Google Maps™ и служба web-почты Gmail™ делают технологию Ajax как захватывающей, так и полезной. Технологии Ajax, включая Hypertext Markup Language (HTML), JavaScript-кодирование, Cascading Style Sheets (CSS), XML и асинхронные Web-запросы, могут создать еще более неотразимые Web-взаимодействия, чем те, которые мы видели в Web V1.0. Естественно, эти технологии существовали со времен Microsoft® Internet Explorer® V4, но только недавно выдающиеся приложения выявили их преимущества.

Насколько тяжело реализовать Ajax? Каждый элемент модели Ajax относительно легко изучить. Но трюк состоит в смешивании всех элементов в целостную систему. Часто проблема усложнена из-за того, что кодирование на стороне клиента и на стороне сервера выполняется различными индивидами. В данной статье рассматривается ситуация, когда всего лишь один человек может написать небольшое Ajax-приложение для просмотра слайдов всего за пару часов.

Слайд-шоу с Ajax

Такие персональные приложения по управлению изображениями как Apple® iPhoto® на Macintosh® популяризировали просмотр слайд-шоу. В них изображения по очереди появляются на определенное время с плавным затуханием и появлением. Кроме того, изображения перемещаются и масштабируются способом, который стал известен под названием Ken Burns Effect.

В этом примере мой браузер загружает с сервера список изображений. Затем я использую этот список изображений, чтобы осуществить показ слайдов при помощи динамического HTML (DHTML). Я оживляю изображения случайными плавными перемещениями, изменениями масштаба и затуханиями, чтобы продемонстрировать привлекательность Ken Burns Effect (Macromedia® Flash и иные "тяжелые" инструменты анимации не используются).


Архитектура

Для понимания отличительных особенностей Ajax вы должны, прежде всего, понимать современную модель Web-программирования. Простое взаимодействие между клиентом и сервером изображено на рисунке 1.

Рисунок 1. Модель взаимодействия клиент-сервер Web V1.0
Рисунок 1. Модель взаимодействия клиент-сервер Web V1.0

Web-браузер, или клиент, выполняет GET или POST-запрос к Web-серверу. Сервер форматирует HTML-ответ. Клиент анализирует HTML и отображает его пользователю. Если пользователь нажимает другую ссылку или кнопку, выполняется следующий запрос к серверу, а текущая страница замещается новой, которую возвратит сервер.

Новая модель более асинхронна (рисунок 2).

Рисунок 2. Модель Ajax взаимодействия клиент-сервер
Рисунок 2. Модель Ajax взаимодействия клиент-сервер

В этой новой модели сервер, как и раньше, возвращает HTML-страницу. Но теперь эта страница содержит некоторый JavaScript-код. Этот код по мере необходимости выполняет запросы к серверу для получения дополнительной информации. Запросы могут быть либо в виде простых GET-запросов для службы Representational State Transfer (REST), либо в виде POST-запросов, необходимых для SOAP.

Затем JavaScript-код анализирует ответ, часто закодированный как XML, и динамически обновляет HTML на странице для отображения новых данных. Кроме XML инженеры возвращают данные, закодированные в формате JavaScript Serialized Object Notation (JSON). Эти данные легче понять браузеру, но не другим типам клиентов. Ценность возврата XML состоит в том, что клиенты, отличные от браузеров, могут интерпретировать данные. Выбор за вами, и зависит он от приложения.


Обслуживание информации об изображении

Первым шагом при разработке слайд-шоу с Ajax является объединение со службой REST-данных. В этом примере я использую PHP-страницу, возвращающую все доступные изображения для слайд-шоу и их размеры (ширина и высота). Все изображения находятся в каталоге images. Имена файлов имеют вид name_width_height.jpg (например, oso1_768_700.jpg означает, что в файле находится изображение Oso (одна из моих собак), имеющее 768 пикселей в ширину и 700 в высоту). Я использую такой тип именования постоянно, поскольку так легко узнать размеры изображения без использования Adobe® PhotoShop® или Macromedia Fireworks.

Для обслуживания списка я использую серверный PHP-код, приведенный в листинге 1.

Листинг 1. Страница slides.php на сервере
<?php
header( "Content-type: text/xml" );
?>
<slides>
<?php
if ($handle = opendir('images')) {

while (false !== ($file = readdir($handle)))
{
        if ( preg_match( "/[.]jpg$/", $file ) ) {
                preg_match( "/_(\d+)_(\d+)[.]/", $file, $found );
?>
<slide src="images/<?php echo $file; ?>" 
  width="<?php echo $found[1]; ?>"
  height="<?php echo $found[2]; ?>" /><?php echo( "\n" ); ?>
<?php
        }
}
closedir($handle);
}
?>
</slides>

Код относительно прост. В начале я устанавливаю тип содержимого в XML. Это очень важно для распознавания браузером страницы как XML-документа и создания объектной модели документа (document object model - DOM) для нее. Код начинает тег <slides> и затем читает каталог images для создания тега <slide> для каждого обнаруженного изображения. Наконец, сценарий закрывает тег <slides>.

Если вы просмотрите страницу в браузере Mozilla® Firefox®, размещенную (в моем случае) на моем localhost в каталоге kenburns, то увидите результат, показанный на рисунке 3.

Рисунок 3. Отображение серверного сценария slides.php
Рисунок 3. Отображение серверного сценария slides.php

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


Извлечение XML

Следующий шаг - написание HTML-страницы (приведенной в листинге 2), которая будет читать данные из службы и проверять работу Ajax-соединения между браузером и сервером. Этот HTML-код, со встроенным JavaScript-кодом, извлекает XML и вставляет предупреждение в текст, возвращаемый сервером.

Листинг 2. Простая Ajax-страница
<html>
<body>
<script>
function processReqChange()
{
 if (req.readyState == 4 && req.status == 200 && req.responseXML != null)
  {
    alert( req.responseText );
  }
}

function loadXMLDoc( url )
{
  req = false;
  if(window.XMLHttpRequest) {
    try {
      req = new XMLHttpRequest();
        } catch(e) {
      req = false;
        }
  }
  else if(window.ActiveXObject)
  {
    try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
      req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
      req = false;
    }
  }
  }
  if(req) {
    req.onreadystatechange = processReqChange;
    req.open("GET", url, true);
    req.send("");
  }
}

loadXMLDoc( "http://localhost/kenburns/slides.php" );
</script>

</body>
</html>

Код забирает XML-содержимое из указанного URL, а затем функция loadXMLDoc начинает Ajax-запрос. Этот запрос отправляется асинхронно для извлечения страницы и возврата результата. Когда запрос завершается, вызывается функция processReqChange с результатом. В данном случае функция processReqChange отображает значение функции responseText в предупреждающем окне. Результат вызова этой страницы в моем браузере Firefox показан на рисунке 4.

Рисунок 4. XML, показанный в предупреждающем окне
Рисунок 4. XML, показанный в предупреждающем окне

Это хорошее начало. Я определенно получаю XML-данные обратно с сервера. Но позвольте отметить несколько моментов. Во-первых, обратите внимание на то, что URL - это абсолютный путь, доменное имя и все. Это единственный правильный для Ajax стиль URL. Серверный код, который записывает Ajax JavaScript-код, всегда создает правильные, полностью сформированные URL.

Вторым моментом, который здесь не очевиден, являются защитные меры предосторожности в Ajax. JavaScript-код не может просто запросить любой URL. URL должен иметь то же доменное имя, что и страница. В данном случае это localhost. Важно отметить, что вы не можете визуализировать HTML с www.mycompany.com, а затем в сценарии получить данные из data.mycompany.com. Оба домена должны совпадать точно, включая субдомены.

Другим интересным элементом является код в loadXMLDoc, который, кажется, делает обратные кувырки для создания объекта request. Зачем так много препятствий? Версии Internet Explorer до 7 не имели встроенного типа объекта XMLHTTPRequest. Поэтому я должен использовать средства управления Microsoft ActiveX®.

Наконец, в функции processReqChange я проверяю readyState на равенство 4, а status на 200. Значение readyState4 означает, что транзакция завершена. Значение status200 означает, что страница корректна. Вы можете также получить сообщение об ошибке 404, если страница не существует, точно так же, как вы видите в браузере. Я здесь не обрабатываю варианты с исключительными ситуациями, поскольку это только пример, но Ajax-код, который вы поставляете, должен обрабатывать запросы, возвращающие ошибки.


Динамическое создание HTML

Перед рассмотрением создания слайд-шоу, я расширю текущий пример функцией processReqChange, создающей XML-таблицу с результатами XML-запроса с сервера. Таким образом, я могу протестировать две вещи: что я могу читать XML-данные, и что я могу создать HTML из них динамически.

В листинге 3 приведен обновленный код, создающий таблицу из возвращаемых XML-данных.

Листинг 3. Улучшенная тестовая страница
<html>
<body>
<table>
<tbody id="dataTable">
</tbody>
</table>

<script>
function processReqChange()
{
 if (req.readyState == 4 && req.status == 200 && req.responseXML != null)
 {
   var dto = document.getElementById( 'dataTable' );

    var items = [];
    var nl = req.responseXML.getElementsByTagName( 'slide' );
    for( var i = 0; i < nl.length; i++ )
    {
      var nli = nl.item( i );
      var src = nli.getAttribute( 'src' ).toString();
      var width = parseInt( nli.getAttribute( 'width' ).toString() );
      var height = parseInt( nli.getAttribute( 'height' ).toString() );

      var trNode = document.createElement( 'tr' );

      var srcNode = document.createElement( 'td' );
      srcNode.innerHTML = src;
      trNode.appendChild( srcNode );

      var widthNode = document.createElement( 'td' );
      widthNode.innerHTML = width.toString();
      trNode.appendChild( widthNode );

      var heightNode = document.createElement( 'td' );
      heightNode.innerHTML = height.toString();
      trNode.appendChild( heightNode );

      dto.appendChild( trNode );
    }
    load_slides( items );
    start_slides();
  }
}

function loadXMLDoc( url )
{
  req = false;
  if(window.XMLHttpRequest) {
    try {
      req = new XMLHttpRequest();
    } catch(e) {
      req = false;
    }
  }
  else if(window.ActiveXObject)
  {
    try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
      req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
      req = false;
    }
  }
  }
  if(req) {
    req.onreadystatechange = processReqChange;
    req.open("GET", url, true);
    req.send("");
  }
}

loadXMLDoc( "http://localhost/kenburns/slides.php" );
</script>

</body>
</html>

При загрузке страницы в браузер должна отобразиться информация, показанная на рисунке 5.

Рисунок 5. Обновленная тестовая страница
Рисунок 5. Обновленная тестовая страница

Обновленный код processReqChange теперь просматривает объект responseXML вместо текста responseText. Кроме того, он использует getElementsByTagName для доступа ко всем тегам <slide>. Затем он выбирает атрибуты src, width и height и использует метод createElement объекта document для создания строк и ячеек для хранения данных. Этот способ использования метода createElement намного более устойчивый, чем метод старой школы, в котором вы создаете HTML-строку с содержимым таблицы и используете innerHTML для добавления данных к существующему элементу.


Создание слайд-шоу

Теперь, когда у меня есть Web-служба, указывающая изображения в слайд-шоу, мне необходим клиентский код для отображения этих слайдов и выполнения анимаций Ken-Burns-Effect. Для этого я должен собрать набор JavaScript-объектов, выполняющих три основные функции:

  1. Инкапсуляция изображения
  2. Обеспечение основного механизма анимации
  3. Выполнение эффектов (например, перемещение, увеличение и затухание)

Инкапсуляция изображения

Начиная с контейнера изображений, я создаю класс ImageInfo, показанный в листинге 4.

Листинг 4. ImageInfo.js
function ImageInfo( src, width, height, htmlObj )
{
  this.src = src;
  this.width = width;
  this.height = height;
  this.current_width = width;
  this.current_height = height;

  this.htmlObj = htmlObj;
  this.htmlObj.src = this.src;
  this.htmlObj.width = this.current_width;
  this.htmlObj.height = this.current_height;
}

ImageInfo.prototype.set_opacity = function( opacity )
{
  this.htmlObj.style.MozOpacity = opacity / 100;
  var f = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+opacity+')';
  this.htmlObj.style.filter = f;
}

ImageInfo.prototype.set_position = function( x, y )
{
  this.htmlObj.style.left = x+'px';
  this.htmlObj.style.top = y+'px';
}

ImageInfo.prototype.set_size = function( w, h )
{
  this.current_width = w;
  this.current_height = h;

  this.htmlObj.width = this.current_width;
  this.htmlObj.height = this.current_height;
}

ImageInfo.prototype.get_image = function()
{
  return this.htmlObj;
}

ImageInfo.prototype.hide = function()
{
  this.htmlObj.style.visibility = 'hidden';
}

ImageInfo.prototype.show = function()
{
  this.htmlObj.style.visibility = 'visible';
}

Для каждого изображения в слайд-шоу существует один соответствующий объект ImageInfo. Этот объект инкапсулирует информацию, известную об изображении: src, width и height. Он также имеет ссылку на HTML-тег <img>, который отображает изображение в документе, а также на удобные вспомогательные методы для перемещения изображения, установки его прозрачности и т.д. Отмечу, что в Firefox и других основанных на Gecko® браузерах для установки прозрачности применяется стиль MozOpacity. В Internet Explorer используется filter.

Создание простого механизма анимации

Теперь я напишу простой механизм анимации. Его код находится в файле Animation.js, приведенном в листинге 5.

Листинг 5: Animation.js
function Animation( am, img, seconds, effects )
{
  this.img = img;
  this.animationManager = am;
  this.seconds = seconds;
  this.effects = effects;
  this.startMS = 0;
}

Animation.prototype.start = function()
{
  this.animationManager.add( this );
  this.startMS = 0;

  this.img.hide();
  for( var e in this.effects )
  {
    this.effects[e].apply( 0 );
  }
  this.img.show();
}

Animation.prototype.animate = function()
{
  var d = new Date();
  if ( this.startMS == 0 )
    this.startMS = d.valueOf();

  var p = (((d.valueOf()-this.startMS)/1000)/this.seconds)*100;
  for( var e in this.effects )
    this.effects[e].apply( p );
}

Animation.prototype.done = function()
{
  var d = new Date();
  return ( ( d.valueOf() - this.startMS ) / 1000 ) > this.seconds;
}

function AnimationManager( speed )
{
   this.animations = [];
   var self = this;
   window.setInterval( function() { self.idle(); }, speed );
}

AnimationManager.prototype.add = function( anim )
{
  this.animations.push( anim );
}

AnimationManager.prototype.idle = function()
{
  if ( this.animations.length > 0 )
  {
    this.animations[0].animate();
    if ( this.animations[0].done() )
      this.animations.shift();
    if ( this.animations.length == 0 )
      this.on_finished();
  }
}

AnimationManager.prototype.on_finished = function()
{
}

В листинге 5 показаны два класса: Animation и AnimationManager. Класс AnimationManager управляет таймером и посылает анимационные сообщения первому элементу в его списке объектов Animation. Когда объект Animation говорит, что он закончил работу, класс переходит к следующему элементу и т.д.

Объект Animation применяет серию эффектов к конкретному изображению определенный период времени, указанный в секундах. Это работа объекта Animation формировать сообщение о проценте выполнения и передавать его в метод apply каждого эффекта. Эффекты затем определяют, что они должны сделать с изображением на основе этих процентов. Например, эффект перемещения знает начальную позицию и конечную позицию, и вычисляет, куда поместить изображение на основе полученных процентов. Если значение равно 50%, то изображение нужно разместить на половине пути между началом и концом.

При подготовке этой статьи я просмотрел большой объем JavaScript-кода анимации. JavaScript-анимация часто критикуется за прерывистость, поскольку все JavaScript-анимации выполняются с использованием метода window.setInterval. Это синхронизирующий метод, в котором вы указываете и интервал обратных вызовов, и функцию обратного вызова. Большая часть кода в Web написана так, что каждый раз при вызове функции анимация перемещается на один шаг. Но на самом деле это не работает, поскольку интервал, который вы указываете браузеру - это только совет. Если вы укажете 20 миллисекунд, вы можете получить вызов через 20 миллисекунд один раз, затем через секунду следующий. Браузеры являются однопоточными, поэтому вы не можете полагаться на таймер.

Решением является использование метода valueOf объекта Date для просмотра того, сколько времени прошло после начала анимации. Эта разница выражается в миллисекундах и используется для вычисления значения процентов, на которое нужно выполнить анимацию со времени отключения таймера setInterval. Этот метод обеспечивает плавную анимацию, которая действительно занимает столько времени, сколько вы указываете.

Выполнение эффектов

Последней частью из трех основных классов является Ken Burns Effects. Эти эффекты применяются к изображению через объект Animation (листинг 6).

Листинг 6: KenBurnsAnimations.js
function KenBurnsFader( img, windowSize )
{
  this.img = img;
  this.windowSize = windowSize;
}

KenBurnsFader.prototype.apply = function( percent )
{
  var opacity = 100;

  if ( percent <= this.windowSize )
    opacity = ( percent / this.windowSize ) * 100;
  else if ( percent >= ( 100 - this.windowSize ) )
    opacity = ( ( 100 - percent ) / this.windowSize ) * 100;

  this.img.set_opacity( opacity );
}

function KenBurnsZoomer( img, start, end, cw, ch )
{
  this.start = start;
  this.end = end;
  this.img = img;

  var wr = this.img.width / cw;
  var nw = this.img.width * wr; 
  var nh = this.img.height * wr; 

  this.sw = ( nw * ( this.start / 100 ) );
  this.ew = ( nw * ( this.end / 100 ) );
  this.sh = ( nh * ( this.start / 100 ) );
  this.eh = ( nh * ( this.end / 100 ) );
  this.dw = ( this.ew - this.sw ) / 100;
  this.dh = ( this.eh - this.sh ) / 100;
}

KenBurnsZoomer.prototype.apply = function( percent )
{
  this.img.set_size(
    this.sw + ( this.dw * percent ),
    this.sh + ( this.dh * percent ) );
}

function KenBurnsMover( img, sx, sy, ex, ey, cw, ch )
{
  this.img = img;
  this.sx = sx / 100;
  this.ex = ex / 100;
  this.sy = sy / 100;
  this.ey = ey / 100;
  this.cw = cw;
  this.ch = ch;
  this.wr = this.img.width / this.cw;
}

KenBurnsMover.prototype.apply = function( percent )
{
  var nw = this.img.current_width * this.wr;
  var nh = this.img.current_height * this.wr;

  var cntw = ( ( this.cw / 2 ) - ( nw / 2 ) );
  var cnth = ( ( this.ch / 2 ) - ( nh / 2 ) );

  var sx = ( nw * this.sx );
  var ex = ( nw * this.ex );
  var sy = ( nh * this.sy );
  var ey = ( nh * this.ey );
  var dx = ( ex - sx ) / 100;
  var dy = ( ey - sy ) / 100;
  var x = cntw + sx + ( dx * percent );
  var y = cntw + sy + ( dy * percent );

  this.img.set_position( x, y );
}

Три эти класса обрабатывают различные эффекты, примененные к изображению. Класс KenBurnsFader обрабатывает плавное исчезновение и появление изображения, используя его прозрачность. Класс KenBurnsZoomer обрабатывает масштабирование изображения с начального уровня до конечного. Класс KenBurnsMover обрабатывает перемещение изображения из начальной позиции в конечную (указанны в процентах от размера изображения).

После некоторых экспериментов я определил, что наиболее привлекательные эффекты движения получаются при перемещении изображения из одного угла в другой относительно середины окна. Метод apply класса KenBurnsMover содержит довольно сложную математику, которая не только перемещает изображение относительно центра содержащего его тега <div>, но также меняет его размер относительно размера тега <div>, так что анимация выглядит маленькой в маленьком окне и большой в большом. Это увеличение основано на высоте окна.

Реализация DHTML без Ajax

Имея эти фундаментальные классы, можно приступить к реализации версии слайд-шоу для тестирования на DHTML без Ajax (листинг 7).

Листинг 7. Слайд-шоу без Ajax
<html>
<head>
<style type="text/css">
body { background: black; margin: 0px; padding: 0px; }
</style>
<script src="KenBurnsAnimations.js">
</script>
<script src="Animation.js">
</script>
<script src="ImageInfo.js">
</script>
<script>
var g_animationManager = new AnimationManager( 50 );
var g_current_slide = 0;
var g_slides = [];
var g_directions = [
{ sx: [ -30, 0 ], ex: [ 5, 40 ], sy: [ -30, 0 ], ey: [ 5, 40 ] }, // nw -> se
{ sx: [ 5, 40 ], ex: [ -30, 0 ], sy: [ 5, 40 ], ey: [ -30, 0 ] }, // ne -> sw
{ sx: [ 5, 40 ], ex: [ -30, 0 ], sy: [ 5, 40 ], ey: [ -30, 0 ] }, // se -> nw
{ sx: [ -30, 0 ], ex: [ 5, 40 ], sy: [ 5, 40 ], ey: [ -30, 0 ] } // sw -> ne
];

g_animationManager.on_finished = function()
{
  g_current_slide++;
  if ( g_current_slide >= g_slides.length )
    g_current_slide = 0;
  g_slides[ g_current_slide ].start();
}

function rnd( start, end )
{
  return ( Math.random() * ( end - start ) ) + start;
}

function load_slides( images )
{
  var ic = document.getElementById( 'imgContainer' );

  for( var i in images )
  {
    var img = images[i];

    var imgObj = document.createElement( 'img' );
    imgObj.style.position = 'absolute';
    imgObj.style.left = '0px';
    imgObj.style.top = '0px';
    imgObj.style.visibility = 'hidden';
    ic.appendChild( imgObj );

    var ii = new ImageInfo( img.src, img.width, img.height, imgObj );

        var szoom = rnd( 50, 100 );
        var ezoom = rnd( 70, 120 );

        var d = parseInt( ( Math.random() * g_directions.length ).toString() );
        var di = g_directions[ d ];
        var sx = rnd( di.sx[0], di.sx[1] );
        var sy = rnd( di.sy[0], di.sy[1] );
        var ex = rnd( di.ex[0], di.ex[1] );
        var ey = rnd( di.ey[0], di.ey[1] );

    g_slides.push( 
      new Animation( g_animationManager, ii, 10,
        [ new KenBurnsZoomer( ii, szoom, ezoom, ic.clientWidth, ic.clientHeight ),
          new KenBurnsMover( ii, sx, sy, ex, ey, ic.clientWidth, ic.clientHeight ),
          new KenBurnsFader( ii, 30 ) ] )
    );
  }
}

function start_slides()
{
  g_slides[ g_current_slide ].start();
}
</script>
</head>
<body>

<div style="position:relative;width:100%;height:100%;overflow:hidden;"
  id="imgContainer">
</div>

<script>
var images = [
{ src: 'images/megan1_875_700.jpg', width: 875, height: 700 },
{ src: 'images/oso1_875_700.jpg', width: 875, height: 700 },
{ src: 'images/oso2_873_700.jpg', width: 873, height: 700 }
];
load_slides( images );
start_slides();
</script>

</body>
</html>

Тяжело показать, как это выглядит в браузере, без видеоролика. Поэтому я сделал снимки слайд-шоу и представил их на рисунке 6.

Рисунок 6. Снимки экрана слайд-шоу
Рисунок 6. Снимки экрана слайд-шоу

Эта страница начинается с получения базовых классов через элементы src тегов <script>. После установки этих классов добавляются новые функции для создания полного механизма: load_slides и start_slides. Функция load_slides принимает массив спецификаций src, width и height изображений и создает теги <image> и анимации. Функция start_slides начинает слайд-шоу с первого элемента.

Еще одна добавленная к менеджеру анимации функция, on_finished, вызывается при завершении процесса анимации. Я использую это уведомление для перемещения к следующему слайду или возврата к первому слайду в списке, если завершен показ всех слайдов.

Возвращаясь назад к load_slides, обратите внимание на массив g_directions. Этот массив содержит набор случайных диапазонов, используемых загрузчиком слайдов для указания позиций, с которых изображение должно начать движение и на которых закончить. Наиболее привлекательные эффекты получаются при перемещении из угла в угол. Как указано в комментариях, эти диапазоны устанавливают движение слайда из каждой комбинации ne (северо-восток), se (юго-восток), nw (северо-запад) и sw (юго-запад). Последний тег <script> определяет массив images и использует функции load_slides и start_slides для запуска слайд-шоу.


Создание слайд-шоу с Ajax

Последним шагом в этом процессе является создание Ajax-версии слайд-шоу. Это означает замену жестко закодированного списка изображений на что-нибудь, извлекаемое из службы slides.php.

Код Ajax-версии слайд-шоу приведен в листинге 8.

Листинг 8. Код Ajax-версии слайд-шоу
<html>
<head>
<style type="text/css">
body { background: black; margin: 0px; padding: 0px; }
</style>
<script src="KenBurnsAnimations.js">
</script>
<script src="Animation.js">
</script>
<script src="ImageInfo.js">
</script>
<script src="SlideShow.js">
</script>
</head>
<body>

<div style="position:relative;width:100%;height:100%;overflow:hidden;" 
  id="imgContainer">
</div>

<script>
function processReqChange()
{
  if (req.readyState == 4 && req.status == 200 
      && req.responseXML != null)
  {
    var items = [];
    var nl = req.responseXML.getElementsByTagName( 'slide' );
    for( var i = 0; i < nl.length; i++ )
    {
      var nli = nl.item( i );
      var src = nli.getAttribute( 'src' ).toString();
      var width = parseInt( nli.getAttribute( 'width' ).toString() );
      var height = parseInt( nli.getAttribute( 'height' ).toString() );
      items.push( { src: src, width: width, height: height } );
    }
    load_slides( items );
    start_slides();
  }
}

function loadXMLDoc( url )
{
  req = false;
  if(window.XMLHttpRequest) {
    try {
      req = new XMLHttpRequest();
    } catch(e) {
      req = false;
    }
  }
  else if(window.ActiveXObject)
  {
    try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
    try {
      req = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
      req = false;
    }
  }
  }
  if(req) {
    req.onreadystatechange = processReqChange;
    req.open("GET", url, true);
    req.send("");
  }
}

loadXMLDoc( "http://localhost/kenburns/slides.php" );
</script>

</body>
</html>

Я переместил функции start_slides и load_slides во внешний JavaScript-файл SlidesShow.js для того, чтобы сократить длину данного кода. Оставшаяся часть кода аналогична тестовой Ajax-странице в листинге 2. Но, вместо вставки предупреждающего окна или добавления элементов в таблицу, этот код создает массив информации о слайдах и затем вызывает load_slides и start_slides.

Вот и все! Имеем Ajax-версию слайд-шоу, использующую Ken Burns Effect для динамического перемещения, масштабирования и изменения прозрачности изображений.


Адаптация слайд-шоу с Ajax для ваших потребностей

В данной статье я использовал объектно-ориентированный JavaScript-код везде, где это было возможно. JavaScript - это полностью объектно-ориентированный язык программирования. Хотя он может не использовать ключевые слова class и interface, он сохраняет ваш код понятным и управляемым. Я также рекомендую использовать, если это возможно, интегрированные среды Ajax. Здесь я не использовал ни одну из них, поскольку хотел показать простое Ajax-решение. Но современные интегрированные среды (их существует не мало) облегчают написание более переносимого Ajax и DHTML-кода.

Кроме того что вы увидели в статье, для ваших слайд-шоу на Ajax я порекомендовал бы следующее:

  • Используйте анимации, основанные на времени. Пошаговые анимации, которые люди пытаются использовать с кодом setInterval, выглядят прерывистыми.
  • Создайте прототип вашего кода в DHTML для визуальных элементов, затем добавьте Ajax-код. При этом подходе вы сможете работать с вашим DHTML-кодом в режиме off-line.
  • Отделите Ajax-код, соединяющий с сервером, от DHTML-компонентов пользовательского интерфейса (UI), который визуализирует данные. Так вы сможете использовать компоненты даже без применения Ajax для получения данных.
  • Используйте функции createElement и appendChild вместо функции innerHTML для вывода содержимого на страницу.
  • Проверяйте ваш клиентский код на всех браузерах, которые хотите поддерживать. Также сохраняйте список проблем совместимости, с которыми вы сталкиваетесь, вместе с описанием метода решения этих проблем. Попытайтесь инкапсулировать исправления клиентского кода в повторно используемые вспомогательные JavaScript-функции и классы.
  • Для усложненных интерфейсов, включающих несколько анимаций, используйте сначала раскадровку, для того чтобы вы могли работать с вашими клиентами и выяснить точно, что они хотят, до начала кодирования. Раскадровка - это анимированная версия спецификации. JavaScript-анимации пишутся быстро, поэтому стоит иметь четкое представление о том, что вы хотите сделать до начала процесса кодирования. В противном случае вы можете сделать много лишней работы.
  • С точки зрения карьеры "прикладные инженеры" мира Web V1.0, сфокусированные на базе данных и бизнес-логике, ограничены в мире Web V2.0. Наступило время понять, что не все запросы к серверу будут предназначены для HTML. Также, Ajax и DHTML - это реальные инструменты для настоящих инженеров, которым платят реальные деньги за их квалификацию. Внешний интерфейс не только для дизайнеров.

Раньше для создания динамичных слад-шоу, подобных рассмотренному в данной статье, требовалась технология Flash или аналогичные технологии. Используя современные браузеры, имеющие отличную поддержку DHTML с развитыми эффектами, такими как прозрачность (или даже вращение, размазывание и др. в Internet Explorer), и Ajax, вы можете делать потрясающие вещи прямо в браузере. Это значит, что ваши пользователи не должны будут загружать необычные расширения или запускать потенциально небезопасные приложения. Они просто могут просматривать ваши страницы и получать великолепные графические эффекты, которые побудят их вернуться на ваш сайт снова.


Загрузка

ОписаниеИмяРазмер
Code and file samples for this articlex-ajaxslideshow/kenburns.zip705KB

Ресурсы

Научиться

Получить продукты и технологии

  • Пробное программное обеспечение IBM: Используйте в вашем следующем проекте пробное программное обеспечение IBM, доступное для загрузки с сайта developerWorks.
  • Apple iPhoto: Посмотрите на вдохновителя написания автором данной статьи, хотя имеются и другие приложения с Ken Burns Effect.

Обсудить

  • Блоги developerWorks: Примите участие в блогах developerWorks и подключайтесь к сообществу developerWorks.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=XML
ArticleID=146544
ArticleTitle=Слайд-шоу на Ajax с DHTML и XML
publish-date=04182006