12. Управление CSS-свойствами на JavaScript

Функция animateCSS

Ниже приводится код функции animateCSS из книги Девида Флэнагана «JavaScript, подробное руководство, 5-ое издание». Эта функция поможет создавать JavaScript-анимацию на базе CSS. Вы можете подключать файл animateCSS.js к своим собственным скриптам. Код функции подробно прокомментирован.


Файл animateCSS.js

// This code is from the book JavaScript: The Definitive Guide, 
// 5th Edition, by David Flanagan. Copyright 2006 O'Reilly Media, 
// Inc. (ISBN #0596101996)
/**
 * ------------------
 * Файл animateCSS.js
 * ------------------
 * Аргументы функции:
 * element      -- ссылка на анимируемый HTML-элемент
 * numFrames    -- общее число кадров в анимации
 * timePerFrame -- количество миллисекунд отображения каждого кадра
 * animation    -- объект, определяющий анимацию
 * whendone     -- необязательная функция, которая будет вызвана после
 *                 завершения анимации. Если функция указана, ей в  
 *                 качестве аргумента передается значение аргумента 
 *                 element 
 * Функция animateCSS просто определяет платформу для анимации. 
 * Выполняемую анимацию задают свойства объекта animation. Каждое 
 * свойство должно иметь то же имя, что и свойство CSS-стиля. Значением
 * каждого свойства должна быть функция, возвращающая значения для 
 * этого свойства стиля. Каждой функции передаётся номер кадра и общий 
 * промежуток времени, прошедший с начала анимации, а функция может 
 * использовать это для вычисления значения стиля, которое она должна 
 * вернуть для данного кадра. Например, чтобы анимировать изображение 
 * так, чтобы оно передвигалось из левого верхнего угла, вы можете 
 * вызвать функцию animateCSS так:
 *
 * animateCSS(image, 25, 50, // Анимировать в течении 25 кадров
 *                           // по 50 mc каждый
 *             { // Установить свойства left и top для каждого кадра:
 *               top: function(frame,time) { return frame*8 + "px"; },
 *               left: function(frame,time) { return frame*8 + "px"; }
 *             });
 **/
function animateCSS(element, numFrames, timePerFrame, 
                                        animation, whendone)
{
  var frame = 0; // Текущий номер кадра
  var time  = 0; // Общее веремя анимации, прошедшее с начала

  // Задать вызов displayNextFrame каждые timePerFrame миллисекунд.
  // Так будет отображаться каждый кадр анимации.
  var intervalId = setInterval(displayNextFrame, timePerFrame);

  // На этом работа animateCSS завершается, но предыдущая строка 
  // гарантирует, что следующая вложенная функция будет вызываться для
  // каждого кадра анимации.

  function displayNextFrame() 
  {
    if (frame >= numFrames) // Проверить, не закончилась ли анимация
    {                                   
      clearInterval(intervalId);       // Если да -- прекратить вызовы,
      if (whendone) whendone(element); // вызвать функцию whendone 
      return;                          // (если задана), и завершить
    }                                  // работу
    // Обойти в цикле все свойства, определяемые объектом анимации
    for(var cssprop in animation) 
    {
      // Для каждого свойства вызвать его функцию, передавая  
      // ей номер кадра и прошедшее время. Используем возвращаемое 
      // функцией значение в качестве нового значения соответствующего
      // свойства стиля для указанного элемента. Используем блок 
      // try/catch, чтобы игнорировать любые исключительные ситуации,
      // возникающие из-за неверных возвращаемых значений.
      try 
      {
        element.style[cssprop] = animation[cssprop](frame, time);
      } catch(e) {}
    }
    frame++;               // Увеличить номер кадра
    time += timePerFrame;  // Увеличить прошедшее время
  }
}

Пояснение фрагмента кода c инструкцией try/catch

Инструкция try/catch реализует механизм обработки исключений (ошибок времени исполнения). Конструкция try размечает фрагмент кода, в котором будут обрабатываться исключения. Конструкция catch работает тогда и только тогда, когда в блоке try возникает исключение (ошибка).

Общий синтаксис:



try 
{
  // Здесь помещают код, за которым ведется наблюдение.
  // Если в коде возникнет исключение, начнет работать блок catch.
} 
catch(e) 
{
  // Инструкции в этом блоке будут работать тогда и только тогда,
  // когда в блоке try возникает исключение (ошибка). Инструкции
  // в этом блоке могут использовать локальную переменную e,
  // ссылающуюся на объект класса Error (детализация ошибки)
}

Инструкция try/catch блокирует появление на экране системного окна с сообщением об ошибке. Она позволяет обработать ошибку программным путем внутри блока catch или игнорировать её (как это сделано внутри animateCSS).

Обратите внимание, в фунции animateCSS создается таймер


var intervalId = setInterval(displayNextFrame, timePerFrame);

который будет вызывать функцию displayNextFrame каждые timePerFrame миллисекунд. Но функция displayNextFrame является внутренней функцией animateCSS, и использует локальные переменные frame и time объемлющей функции. Что это означает? Это означает, что образуется замыкание, и объект вызова функции animateCSS не будет уничтожен после завершения её работы.

Построим несколько примеров с использованием функции animateCSS. Для этого запишем приведенный выше код Девида Флэнагана в файл animateCSS.js, и подключим его к странице обычным способом при помощи элемента SCRIPT в головной части HTML-кода. Начнём с повторения примера 2 «Веселая кнопка».

Пример 4. Веселая кнопка 2

Рассмотрим следующие две задачи:

  1. Выдвинуть элемент от границы или задвинуть за границу родительского блока (элемент перемещается). Части элемента за границами родительского блока невидимы (отсекаются).
  2. Постепенно открыть (спрятать) элемент (сам элемент неподвижен, двигается «шторка», которая его открывает или закрывает).

Первая задача решается с помощью CSS-свойства overflow со значением hidden и позиционированием элемента за пределы (из пределов) родительского блока.

Вторая задача решается при помощи динамического изменения значения свойства clip (шторка), заданного для родительского элемента.

overflow

Свойство определяет, как показывать содержимое блока, которое в нем не помещается.

Свойство можно применять только к блочным или строчным замещаемым элементам (картинкам, например).

Значения:

  • visible — значение по умолчанию. Содержимое будет видимо за границами блока.
  • hidden — содержимое отсекается краями блока.
  • scroll — содержимое отсекается краями блока, но у блока появляются постоянные полосы прокрутки, и содержимое можно увидеть.
  • auto — работает как scroll, если содержимое выходит за границу блока. Если содержимое вмещается в блок, полосы прокрутки не отображаются.
clip

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

Значения:

  • auto — работает по умолчанию. Область отсечения совпадает с размерами элемента, для которого свойство задано.
  • rect(top right bottom left) — задает прямоугольную область отсечения. При этом значения top, right, bottom и left означают расстояния от верхнего левого угла элемента, для которого свойство задано. В качестве значений top, right, bottom и left можно использовать единицы размера и auto (устанавливает размер по соответствующему размеру элемента).

Пусть блок box имеет размеры 300x200. Приведенное ниже определение задает область отсечения размером 150х100, расположенную в левом верхнем углу блока box:


#box
{
  position:absolute;
  width:300px;
  height:200px; 
  clip: rect(0 150 100 0);
}      

Вернемся к двум поставленным задачам:

  1. Выдвинуть элемент от границы или задвинуть за границу родительского блока (элемент перемещается). Части элемента за границами родительского блока невидимы (отсекаются).
  2. Постепенно открыть (спрятать) элемент (элемент неподвижен, двигается «шторка»).

Запишем HTML-код:


<DIV id="parentbox">
  <DIV id="box">
    <IMG id="pic" src="pic/01.png" width=500 height=313 alt="" title="">
  </DIV>
</DIV>

В стилевых определениях (в CSS-файле) пропишем относительное позиционирование для блока parentbox и абсолютное для блока box. Элемент IMG является содержимым блока box.

Относительное позиционирование для parentbox обеспечит две важные вещи: во-первых, блок parentbox останется в потоке, во-вторых, координаты абсолютно позиционированного блока box будут отсчитываться от родителя parentbox.

Будем решать две поставленные задачи по отношению к картинке — содержимому блока box.

Первая задача решается при помощи правила overflow:hidden, заданного для блока box и созданием анимации, которая будет управлять свойством top элемента IMG.

Вторая задача решается при помощи правила clip: rect(0 auto x 0), в котором x меняется в анимационном цикле (от 0 до высоты картинки, при её открытии, и от высоты картинки до 0 при её сокрытии).

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

Пример 5. Показать картинку