Сегодня мы разберём, как реализуется эффект Explode без использования каких-либо дополнительных библиотек.
→ Демо-страничка: Explode.js
Что требуется сделать:
Разрезаем элемент
Это самая простая часть. Дано: некий элемент (например, картинка или блок с текстом) для взрывания и количество ячеек N*N. Пишем цикл, в котором будем создавать N*N дивов и помещать, а затем сдвигать клон нашего элемента.
Объявляем нужные для работы переменные:
Создаём ячейки:
Задаём ячейке необходимые стили (этот код уже внутри цикла):
Теперь вставляем в каждую ячейку клон элемента:
Сейчас надо сдвинуть клон в каждой ячейке, чтобы совокупность всех ячеек выглядела точно так же, как цельная картинка (сейчас же в каждой ячейке просто её верхний левый угол).
Т.е. сдвигаем вверх на высоту ячейки, помноженную на номер ячейки по вертикали. Аналогично сдвигаем влево. -pos.top и -pos.left нужны на случай, если элемент сдвинут относительно края окна (а он, скорее всего, сдвинут). Если не отнять эти величины, то в ячейке будет пустота либо не тот кусок элемента — так как учитываются и «родные» отступы.
Первая половина задачи выполнена — картинка «разрезана» на ячейки, с каждой из которых можно работать по отдельности (а именно анимировать в разные стороны), но при этом для пользователя картинка выглядит без изменений.
Теперь надо определить, в каком направлении анимировать каждую из ячеек. Т.е. верхняя левая должна улетать вверх и влево, ячейка справа от неё — просто вверх и т.д., тем самым создавая эффект «взрыва». Надо бы написать вспомогательную функцию direction(i, j, N), где i и j — номер ячейки (в цикле), а N — кол-во ячеек по вертикали и горизонтали.
Попытка №1
Сначала была идея возвращать некую цифру (например, 0 — вверх, 1 — вверх и вправо и т.д. по часовой стрелке) и по ней определять направление. Это на первый взгляд показалось удобным и интуитивным, но на деле породило кучу повторяющегося кода в стиле:
Я даже боюсь представить, как это анимировать. Писать if для каждого случая? Нет, нужно что-то другое.
Этот способ имеет два недостатка:
— громоздкость
— не совсем корректные значения. Например, при N=5 верхняя левая ячейка анимируется влево и вверх, тут всё правильно, однако следующая справа от неё ячейка будет улетать строго вверх, хотя хотелось бы вверх и немного влево (но не так сильно)
Попытка №2
Создать двумерный массив N*N, заполнить его нулями, назначить элементу (i, j) значение 1, и «искать» его, рекурсивно срезая с массива по одному слою (т.е. делая из массива 5х5 массив 4х4 и так далее). Очевидный плюс — нет кучи if-ов и громоздкого кода. Минус всё тот же — не совсем нужные значения.
К сожалению, старые исходники я не сохранил, а писать эту энигму снова нет никакого желания — поэтому на этот раз без примеров исходников.
Попытка №3, успешная
Векторы. Это компактное и удобное решение, которое также решает проблему с «не совсем корректными значениями». Мы будем узнавать, как далеко ячейка находится от центра, и вычислять некий вектор (x, y), и основываясь на x и y, анимировать ячейку по направлениям.
Как же всё было просто…
Для большей наглядности — какие значения задаются ячейкам при N=7:

Дело осталось за малым: анимировать каждую ячейку в заданном направлении. В теле цикла вызываем вспомогательную функцию animateStart для каждой ячейки:
где первым аргументом передаётся сама ячейка, вторым и третьим — её начальные координаты, а последним — вектор (x, y), который представлен массивом из двух чисел.
Теперь приступим к написанию функции animateStart.
Теперь напишем функцию animate. Это — прогресс анимации. Она будет запускаться каждые 10 миллисекунд, ненамного сдвигая ячейку по направлениям. Так и создаётся эффект плавной анимации.
Последний штрих: удаляем оригинальный элемент.
Плагин готов! Демо-страничка в самом начале статьи, а js-исходник находится тут.
→ Демо-страничка: Explode.js
Что требуется сделать:
- Разрезать элемент на N*N ячеек
- Поместить в каждую ячейку копию «взрываемого» элемента
- Сместить margin-ами содержимое каждой ячейки
- Анимировать каждую ячейку в нужном направлении
Разрезаем элемент
Это самая простая часть. Дано: некий элемент (например, картинка или блок с текстом) для взрывания и количество ячеек N*N. Пишем цикл, в котором будем создавать N*N дивов и помещать, а затем сдвигать клон нашего элемента.
Объявляем нужные для работы переменные:
function Explode( elem, N ) { N = N || 3; var pos = elem.getBoundingClientRect(); var top = pos.top; var left = pos.left; var width = Math.round( elem.clientWidth / N ); var height = Math.round( elem.clientHeight / N ); // [...]
Создаём ячейки:
for ( var i = 0; i < N; i++ ) { for ( var j = 0; j < N; j++ ) { var tile = document.createElement( "DIV" ); document.body.appendChild( tile ); // [...] } }
Задаём ячейке необходимые стили (этот код уже внутри цикла):
tile.style.position = "fixed"; tile.style.width = width + "px"; tile.style.height = height + "px"; tile.style.top = (top + height * i) + "px"; tile.style.left = (left + width * j) + "px"; // поскольку мы хотим показывать в ячейке лишь часть элемента, // то скрываем всё что не влезает tile.style.overflow = "hidden";
Теперь вставляем в каждую ячейку клон элемента:
var tileContent = elem.cloneNode( true ); tile.appendChild( tileContent );
Сейчас надо сдвинуть клон в каждой ячейке, чтобы совокупность всех ячеек выглядела точно так же, как цельная картинка (сейчас же в каждой ячейке просто её верхний левый угол).
tileContent.style.marginTop = (-height * i - pos.top) + "px"; tileContent.style.marginLeft = (-width * j - pos.left) + "px";
Т.е. сдвигаем вверх на высоту ячейки, помноженную на номер ячейки по вертикали. Аналогично сдвигаем влево. -pos.top и -pos.left нужны на случай, если элемент сдвинут относительно края окна (а он, скорее всего, сдвинут). Если не отнять эти величины, то в ячейке будет пустота либо не тот кусок элемента — так как учитываются и «родные» отступы.
Первая половина задачи выполнена — картинка «разрезана» на ячейки, с каждой из которых можно работать по отдельности (а именно анимировать в разные стороны), но при этом для пользователя картинка выглядит без изменений.
Теперь надо определить, в каком направлении анимировать каждую из ячеек. Т.е. верхняя левая должна улетать вверх и влево, ячейка справа от неё — просто вверх и т.д., тем самым создавая эффект «взрыва». Надо бы написать вспомогательную функцию direction(i, j, N), где i и j — номер ячейки (в цикле), а N — кол-во ячеек по вертикали и горизонтали.
Попытка №1
Сначала была идея возвращать некую цифру (например, 0 — вверх, 1 — вверх и вправо и т.д. по часовой стрелке) и по ней определять направление. Это на первый взгляд показалось удобным и интуитивным, но на деле породило кучу повторяющегося кода в стиле:
function direction (i, j, N) { if ( i == 0 && j == 0 ) return 7; // влево и вправо if ( i == 0 && j < N - 1 ) return 0; // только вверх if ( i == 0 && j == N - 1 ) return 1; // вверх и влево // [...]
Я даже боюсь представить, как это анимировать. Писать if для каждого случая? Нет, нужно что-то другое.
Этот способ имеет два недостатка:
— громоздкость
— не совсем корректные значения. Например, при N=5 верхняя левая ячейка анимируется влево и вверх, тут всё правильно, однако следующая справа от неё ячейка будет улетать строго вверх, хотя хотелось бы вверх и немного влево (но не так сильно)
Попытка №2
Создать двумерный массив N*N, заполнить его нулями, назначить элементу (i, j) значение 1, и «искать» его, рекурсивно срезая с массива по одному слою (т.е. делая из массива 5х5 массив 4х4 и так далее). Очевидный плюс — нет кучи if-ов и громоздкого кода. Минус всё тот же — не совсем нужные значения.
К сожалению, старые исходники я не сохранил, а писать эту энигму снова нет никакого желания — поэтому на этот раз без примеров исходников.
Попытка №3, успешная
Векторы. Это компактное и удобное решение, которое также решает проблему с «не совсем корректными значениями». Мы будем узнавать, как далеко ячейка находится от центра, и вычислять некий вектор (x, y), и основываясь на x и y, анимировать ячейку по направлениям.
function direction( i, j, N) { return [ (N-1) / 2 - i, j - (N-1) / 2 ] };
Как же всё было просто…
Для большей наглядности — какие значения задаются ячейкам при N=7:

Дело осталось за малым: анимировать каждую ячейку в заданном направлении. В теле цикла вызываем вспомогательную функцию animateStart для каждой ячейки:
animateStart( tile, left + width * j, top + height * i, direction( i, j, N ) );
где первым аргументом передаётся сама ячейка, вторым и третьим — её начальные координаты, а последним — вектор (x, y), который представлен массивом из двух чисел.
Теперь приступим к написанию функции animateStart.
function animateStart( elem, posX, posY, dir ) { var vY = dir[0]; // вектор по вертикали var vX = dir[1]; // вектор по горизонтали var start = new Date().getTime(); // время начала анимации // toX и toY - конечные координаты // например, у верхней левой ячейки вектор (3, -3), таким образом // в этом примере она сдвинется на 150 пикселей вверх и // на столько же влево var toY = posY - 50 * vY; var toX = posX + 50 * vX; setTimeout( animate, 10 ); // запускаем анимацию // [...]
Теперь напишем функцию animate. Это — прогресс анимации. Она будет запускаться каждые 10 миллисекунд, ненамного сдвигая ячейку по направлениям. Так и создаётся эффект плавной анимации.
function animate () { // прогресс анимации, в начале - 0, в конце - 1 var m = (new Date().getTime() - start) / 500; if (m > 1) m = 1; elem.style.top = (posY + (toY - posY) * m) + "px"; // сдвигаем ячейку elem.style.left = (posX + (toX - posX) * m) + "px"; // по стандартной формуле elem.style.opacity = 1 - m; // а также делаем эффект "затухания", добавляя прозрачность if (m < 1) setTimeout( animate, 10 ); // если прогресс анимации меньше 1, запускаем функцию снова }
Последний штрих: удаляем оригинальный элемент.
elem.parentNode.removeChild( elem );
Плагин готов! Демо-страничка в самом начале статьи, а js-исходник находится тут.
