Створення панорам і прокрутка фону з використанням елемента canvas

14

Від автора: Я збираюся створити просту 2D гру на чистому JavaScript. Для першого кроку я хочу показати, як анімувати (панорамувати або прокручувати) фонове зображення, використовуючи елемент canvas. Я також збираюся показати основний код, необхідний для створення циклу, в якому ми зможемо промальовувати кадри.

Існують два поширених сценарію для простих 2D ігор:

Для всього рівня використовується одне величезне зображення. Всі дії відбуваються на одному і тому ж фоні, але змінюється положення вікна перегляду

Використовується невелике зображення, яке постійно прокручується (зазвичай справа наліво) під час того як гравець просувається

Панорамування вікна перегляду всередині фонового зображення

Демо

Натисніть на кнопку, щоб запустити анімацію

Як це працює

У нас є функція, яка викликається для кожного кадру, який вимальовує наша гра. Використовуючи цей метод, ми обчислюємо положення вікна перегляду. У цьому базовому прикладі я вибрав обчислювати положення на основі минулого часу. Таким чином, камера рухається по еліптичним шляху.

function draw(delta) {
totalSeconds += delta;
var x = -1 * (img.width — canvas.width) / 2 * (1 + Math.cos(totalSeconds / Math.PI));
var y = -1 * (img.height — canvas.height) / 2 * (1 + -Math.sin(totalSeconds / Math.PI));
context.drawImage(img, x, y);
}

Нескінченна прокрутка фонового зображення

У другому випадку фонове зображення прокручується безперервно у міру того, як проходить час і просувається гравець. Це схоже на гру Маріо, тільки камера направлена на Маріо весь час.

На анімації вище ми можемо бачити, що для цього ефекту нам знадобляться мінімум два або більше зображень (можна вставити одне зображення декілька раз), в залежності від розміру вікна перегляду.

Демо

Натисніть на кнопку, щоб запустити анімацію. Вертикальні чорні лінії позначають межі кожного зображення. У цьому прикладі я використовую одне і те ж зображення, при цьому швидкість прокрутки постійна – 100 пікселів в секунду.

Як це працює

Положення фону також грунтується на минулому часі (постійна швидкість)

Ми обчислюємо кількість зображень, необхідних для заповнення вікна: Math.ceil(canvas.width / img.width) + 1

Ми обчислюємо поточне положення по горизонталі: totalSeconds * vx % img.width. Зверніть увагу на оператор %

Ми зберігаємо поточний стан контексту і переводимо об’єкт canvas, щоб полегшити промальовування

Ми робимо одне зображення за іншим

Ми відновлюємо значення контексту

function draw(delta) {
totalSeconds += delta;
var vx = 100; // the background scrolls with a speed of 100 pixels/sec
var numImages = Math.ceil(canvas.width / img.width) + 1;
var xpos = totalSeconds * vx % img.width;
context.save();
context.translate(-xpos, 0);
for (var i = 0; i < numImages; i++) {
context.drawImage(img, i * img.width, 0);
}
context.restore();
}

Весь код, що викликається функція draw()

Для того, щоб це працювало нам потрібно провести ще кілька дій. Ось базовий код, який я використовував в цих прикладах.

Я використовував базове полизаполнение requestAnimationFrame polyfill

Анімація починається тільки якщо зображення успішно завантажено (onload)

Логіка запуску/зупинки і кнопка

І функція циклу loop(), яка викликається в кожному кадрі під час анімації. Тут нам потрібна функція requestAnimationFrame().

(function() {
window.requestAnimationFrame = window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| function(callback) { window.setTimeout(callback, 1000 / 60); };
var canvas = document.getElementById(‘bg’);
var context = canvas.getContext(‘2d’);
var looping = false;
var totalSeconds = 0;
var img = new Image();
img.onload = imageLoaded;
img.src = ‘IMG_SOURCE’;
function imageLoaded() {
draw(0);
var btn = document.getElementById(‘btnStart’);
btn.addEventListener. (‘click’, function() {
startStop();
});
}
var lastFrameTime = 0;
function startStop() {
looping = !looping;
if (looping) {
lastFrameTime = Date.now();
requestAnimationFrame(loop);
}
}
function loop() {
if (!looping) {
return;
}
requestAnimationFrame(loop);
var now = Date.now();
var deltaSeconds = (now — lastFrameTime) / 1000;
lastFrameTime = now;
draw(deltaSeconds);
}
function draw(delta) {
/* Тут happens some magic. */
}
}());

Щоб побачити повний вихідний код, перейдіть на наступні сторінки

Панорамування вікна перегляду всередині фонового зображення

Прокрутка фонового зображення