Анимация спрайтов при помощи CSS, JS и Canvas

Всем привет. Пару дней назад совершенно случайно наткнулся в залежах дисков на «Космические рейнджеры 2: Доминаторы». Устанавливать не стал, так как сейчас не хватает времени, чтобы как следует в неё погрузиться. А решил посмотреть, что на диске находится. Посмотрел «Фан-Арт» и там увидел программку для ковыряния ресурсов рейнджеров. Вот и решил посмотреть, из чего же сделаны наши доминаторы. Покликав немного, нашел файлики с анимацией в формате GAI. Начал любоваться той анимацией. Захотел их сохранить в «гифки», но не как не давала та программка сохранить анимацию? Можно либо сохранить текущий кадр, либо все файлы в PNG. Я решил сохранить все кадры, а их было — 150. Картинки все есть, а почему бы не сделать с ними ту же анимацию.

Спрайт


Для того чтобы порадовать себя такой анимацией взял в помощь CSS + JS. А что мне делать со 150-ю файлами изображений? Их вес не критичен, хоть и весят они в сумме больше мегабайта. Главные проблемы с их одновременной загрузкой и манипуляцией. Поэтому я решил «склеить» их в одну. Грузить только одну, да и при помощи CSS + JS придется только правильно позиционировать её.
Осталось выбрать метод «склейки». Я же программист, а мы все ленивые:), поэтому я сразу отбросил ручную склейку в графическом редакторе. Первым делом я бросился, как обычно, к PHP и библиотеке GD. Но она оставляет желать лучшего по работе с полупрозрачными PNG. Дальше я подумал, а чем же ещё можно «склеить» картинки? И остановился на том, что сейчас у всех на слуху, что считается сейчас модным — HTML5. За работу с графикой у него отвечает — Canvas, причем мне очень нравится эта «вкусняшка». Вот поэтому все же решил «клеить» на «холсте».
И так добавлю я ка этот тег в HTML:

<canvas id="sprite">Не поддерживается</canvas>

В JS укажу маску для имени картинки, первую картинку и номер последней (мне не пришлось переименовывать картинки, так как они все идут по порядку). Это должно выглядеть примерно так:

var first = '000';  //номерная часть имени первой картинки
var last  = 49;     //номер последней картинки
var num   = 0;      //итератор

var maskFileName = ['2HULL_PEOPLE_P_A_', '.png'];//маска имени картинки
var dir = 'ship';   //директория, в которой лежат картинки


Теперь можно указать размеры спрайта который должен получиться, и получить его контекст:

var canvas = document.getElementById("sprite"); //выберем наш канвас
canvas.width  = (last + 1) * 75; //задаем ширину, в зависимости от количества
canvas.height = 75;
var width = 0;  //переменная, в которую будем записывать сдвиг
var context = canvas.getContext("2d"); //получаем контекст


Для облегчения перевода из числа в строку (аналог str_pad из PHP) написал функцию-преобразователь с диким названием — zerofikator():

function zerofikator(int, length) { //на вход получаем число и длину строки
	var prefix = '';
	for (var i = num.toString().length; i < length; i++) {
		prefix += '0';
	}
	return prefix + num;
}


Далее для рисования на «холсте» нашими картинками, т.е. создания спрайта, вот такая незатейливая функция:

function draw() {
	var img = document.createElement('img'); /* каждый раз при вызове этой функции мы создаем новый обьект-изображение */
	img.onload = function () {
			  	//когда изображение загрузится рисуем им на канвасе
			  	context.drawImage(img, width, 0);
			  	width += 75; 
//при этом каждый раз сдвигаем на ширину изображения, чтобы рисовать справа от предыдущего рисунка, а не на нем
			  	if (zerofikator(num, first.length) != zerofikator(last, first.length)) {
//проверяем достигли ли мы последнего рисунка
			  		num++; //увиличуем итератор
			  		draw(); //и запускаем функцию вновь
			  	} 		  	
	}
	img.src = dir + '/' + maskFileName[0] + zerofikator(num, first.length) + maskFileName[1]; //собираем имя файла картинки для загрузки
}

draw(); //вызываем функцию впервые


После запуска такой страницы мы увидим широкое покадровое изображение, которое, если мы сохраним — сможем назвать спрайтом. Кстати, сохраненный спрайт весит 615 KB, а 150 картинок 1 189 KB, хм, вот и ещё один плюс:).

Решил добавить преобразование сразу в файл по клику на канвас (решает проблему сохранения для некоторых браузеров):

canvas.onclick = function () {
    window.location = context.canvas.toDataURL('image/png');
};


image

Демо

Анимация


Ну а теперь, можно приступить и к анимации.
В HTML добавляем пару «дивчиков», с которыми мы дальше будем работать:

<div id="gun"></div>
<div id="ship"></div>


Дальше я написал функцию, которая анимирует спрайты, но настройки решил вынести отдельно для того, чтобы можно было его использовать сразу для нескольких «анимашек»:

var styles = {};
styles.cursor = 'pointer'; //чтобы видно было что это наш элемент, меняем курсор
styles.width = '75px'; //размеры элемента
styles.height = '75px';
var elementId = 'gun';   // id элемента, в котором будет анимация
var imgName = 'canvas.png'; // имя файла спрайта


А далее сама функция:

function spriteAnimation(elementId, imgName, styles) {
	
	var img = document.createElement('img');
	var offset = 0;
	img.onload = function () { //как только спрайт загружается
		var element = document.getElementById(elementId);
		element.style.cursor     = styles.cursor;
		element.style.width      = styles.width;
		element.style.height     = styles.height;
		element.style.background = "url('" + imgName + "') " + offset + "px 50%"; //меняем стили для нашего элемента
		var i = 0;
		element.onmouseover = function() { //вешаем обработчик на наведение мыши
			interval = setInterval(function() { //запускаем интервал
				if (offset < img.width) { //для смены позиции изображения
					i++; // если дошли до конца спрайта
				} else {
					i = 0; // то возвращаемся к началу
				}
				offset = 75 * i; //сдвиг по слайду
				element.style.background = "url('" + imgName + "') " + offset + "px 50%"; 
//меняем позиционирование спрайта
			} , 1000/24) //24 кадра в секунду
		}
		element.onmouseout = function(){ //вешаем обработчик на убирание курсора мыши
			clearInterval(interval) //удаляем интервал (прекращаем анимацию)
		}
	}
	img.src = imgName; //даем имя нашего спрайта
}


Ах да, нужно ведь ещё вызвать эту функцию:

spriteAnimation(elementId, imgName, styles);
spriteAnimation('ship', 'ship.png', styles);


И, чтобы смотрелось к месту, можно добавить картинку с фоном и правильно спозиционировать:

<img src="fon.png" />
<div class="conteiner"><div id="gun"></div></div>


image

Демо

Исходники

Как говорится — «Just for fun». А как вы проводите своё время дождливыми осенними вечерами?
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 20
  • +3
    А вы бы и анимацию спрайта в canvas уложили.
    • +3
      • +6
        Отлично, спасибо!
        Даешь ренджеров на canvas с мультеплеером =)
        • 0
          О-да (: приятные воспоминания о рейнджерах.
          И пример подробный, спасибо, naxel
          • 0
            Были беларуские ребята, начинали делать похожую игрушку far7.by, взяли приз на конкурсе от Mozilla — winner of the Best Technology award in the Mozilla Labs Game On 2010 competition. Но что-то давно про них не слышно. Еще тогда успел пощупать какой-то прототипный билд, очень красиво выглядело и многообещающе, а сейчас даже сайт уже не работает =(
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Быть может можно было бы упростить решения, использовав для анимации экспериментальный CSS.
              • 0
                Спасибо! Было интересно почитать
                • 0
                  Впервые встретил эту идею (смены нескольких статичных изображений вместо одного анимированного) тут: pepelsbey.net/2010/12/darkbox-return/

                  А тут прям эволюция в действии)
                  • +3
                    лол, а кино ты никогда не смотрел?
                    • +1
                      Кино написанное на CSS? Нет, не видел :)
                      • +3
                        Ну принцип тот же
                  • +5
                    Спасибо, очень интересно, только смущает реклама в примерах.
                    • 0
                      Меня тоже смущает, извините, хотел по-быстрому выложить примеры, но не нашел хороший хостинг.
                    • 0
                      Извините, а подскажите, что за пушка первая? никогда такой не встречал…
                    • 0
                      Не пойму что это за генератор поля такой, он в игре был?

                      И да, статья полезная.
                      • +1
                        Насколько я помню, был.
                        Но может появился только в «Перезагрузке».
                        • 0
                          Генератора и сканера таких в оригинальных не было, появились только в «Перезагрузке».

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое