БлогNot. Несколько анимаций в одной картинке или Послушная кошка

Несколько анимаций в одной картинке или Послушная кошка

Как показать несколько разных анимаций (фреймов) в элементе canvas, если все фреймы хранятся в одной картинке (каждый - в своём "столбце" этой картинки) по принципу

фрейм_1_спрайт_1 фрейм_2_спрайт_1 фрейм_3_спрайт_1
фрейм_1_спрайт_2 фрейм_2_спрайт_2 фрейм_3_спрайт_2
фрейм_1_спрайт_3 ...              фрейм_3_спрайт_3
...                               фрейм_3_спрайт_4
                                  ...

Количество спрайтов во фрейме может варьироваться, спрайты одинаковы по размеру, необязательно использовать их все, если вы просто взяли готовую картинку из сети как я, но хочется описать всю графику в одном контейнере Javascript.

Нам понадобится одна картинка и одна функция, выполняемая по загрузке страницы. Для смены анимаций мы подготовим кнопки, действия которым назначим по загрузке картинки. Одна кнопка будет "особой", а с последнего кадра её анимации (в нашем случае, с сидящей кошки) начнётся работа приложения.

Вот что получилось:

Ниже этот код показан в виде файла .html в кодировке Юникода UTF-8.

<!DOCTYPE html>
<html lang="ru">
 <head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>Кошка</title>

<style>
 /* обёртка скрипта */
 #catwrapper {
  text-align: center;
  font-family: sans-serif;
  width: 300px;
  margin: 0 auto;
 }
 /* общий стиль кнопок действий */
 .catbutton {
   padding: 1em;
   text-transform: uppercase;
   background-color: transparent;
   border: 1px solid black;
 }
 /* настройки отдельных кнопок действий */
 .catbutton#walk:hover, .catbutton#walk:active, .catbutton#walk:focus {
  background-color: rgba(30, 215, 30, 0.7);
 }
 .catbutton#stop:hover, .catbutton#stop:active, .catbutton#stop:focus {
  background-color: rgba(215, 30, 30, 0.7);
 }
 .catbutton#run:hover, .catbutton#run:active, .catbutton#run:focus {
  background-color: rgba(215, 215, 30, 0.7);
 }
 .catbutton#leap:hover, .catbutton#leap:active, .catbutton#leap:focus {
  background-color: rgba(30, 215, 215, 0.7);
 }
</style>

</head><body>

<div id="catwrapper">
 <canvas id="catcanvas" width="300" height="150"></canvas>
 <div>
  <button class="catbutton" id="walk">Иди</button>
  <button class="catbutton" id="stop">Стой</button>
  <button class="catbutton" id="run">Беги</button>
  <button class="catbutton" id="leap">Скачи</button>
 </div>
</div>

<script>
 window.addEventListener("load", function() { 
  const canvas = document.getElementById('catcanvas');
  const context = canvas.getContext('2d');
  const image = document.createElement('img');
  
  image.src = 'catframes.png'; //Картинка с фреймами, укажите URL своей!
  const w = 197;
  const h = 98.15; // Размер канвы не равен размеру фрейма 197 x 98 px
  
  //Просто опишите здесь свои спрайты по своей картинке, 
  //указав "номер столбца" картинки и индекс последнего спрайта в столбце (отсчёт с нуля)
  //Порядок кнопок соблюдать необязательно, использовать все столбцы тоже.
  const sprites = {
   walk: {spriteIndex: 0, maxFrame: 11},
   stop: {spriteIndex: 1, maxFrame: 5},
   run: {spriteIndex: 2, maxFrame: 11},
   leap: {spriteIndex: 3, maxFrame: 12},
  };

  const timer = 1000 / 15; //основная частота смены картинок
  let interval = 0; //интервал для таймера

  // Начальный кадр будет последним из спрайта "стоп"
  let endFrame = 0;
  let step = 1;
  let {spriteIndex, maxFrame: currentFrame} = sprites.stop; //текущее действие

  const drawFrame = (sprite, spriteFrame) => { //Нарисовать спрайт
   context.clearRect(0, 0, canvas.width, canvas.height);
   context.drawImage(image, w * sprite, h * spriteFrame, w, h, 0, 0, canvas.width, canvas.height);
  };

  const animate = () => { //Анимация фреймов, выполняется при установленном интервале
   drawFrame(spriteIndex, currentFrame);
   if (currentFrame !== endFrame) currentFrame += step;
   else currentFrame = 0;
  };

  const playSprite = (sprite, callback) => { //Отрисовка спрайта вне установленного интервала
   if (currentFrame !== endFrame + step) {
    drawFrame(spriteIndex, currentFrame);
    currentFrame += step;
    setTimeout(() => { playSprite(sprite, callback); }, timer);
   } 
   else {
    callback && callback();
   }
  };

  const loadSprite = (sprite, spriteStep = 1) => { //Загрузка спрайта, шаг = spriteStep
   currentFrame = 0;
   step = spriteStep;
   ({spriteIndex, maxFrame: endFrame} = sprites[sprite]);
   if (step === -1) { //Смена направления, если нужно
    [currentFrame, endFrame] = [endFrame, currentFrame];
   }
  };

  const updateSprite = sprite => { //Обрабатывает нажатия кнопок в зависимости от состояния анимации
   if (sprite !== 'stop') { //Не "стоп"
    if (!interval) { //Интервал не установлен
     loadSprite ('stop', -1); //Поднять кошку, проиграв фрейм обратно
     playSprite ('stop', () => {
      loadSprite(sprite);
      interval = setInterval(animate, timer);
     });
    } 
    else { //Интервал установлен, поменять движение
     loadSprite(sprite);
    }
   } 
   else if (interval) { //"Стоп"
    //Если во время анимации, завершить её и сыграть "стоп"
    clearInterval(interval);
    interval = 0;
    loadSprite('stop');
    playSprite('stop');
   }
   //"Стоп" нажата, когда интервал не установлен - ничего не делать
  };

  image.addEventListener('load', () => { //На загрузку картинки
   drawFrame(spriteIndex, currentFrame);
   const buttons = document.getElementsByTagName('button');
   [...buttons].forEach(button => { //Установить обработчики кнопок фреймов
    button.addEventListener('click', e => {
     //Апдейтить, если действие кнопки отличается от текущего спрайта
     if (spriteIndex !== sprites[e.target.id].spriteIndex) {
      updateSprite(e.target.id);
     }
    });
   });
  });
 });
</script>
<noscript>
 <p>Включите Javascript в браузере для работы приложения.</p>
</noscript>

</body> </html>

 Картинка catframes.png с фреймами (208 Кб)

30.05.2024, 11:10 [53 просмотра]


теги: графика javascript картинка время

К этой статье пока нет комментариев, Ваш будет первым