Рецепт вращения планет в космосе на HTML5 + JavaScript

imageВ рамках создания нашей браузерной космической игры, перед нами стояла задача разработать простую и наименее ресурсозатратную анимацию вращения планет в звездной системе.

Вычеркиваем


После непродолжительного подбора различных способов реализации, сразу были исключены варианты:

  • с gif-анимацией (из-за низкого качества изображения);
  • с Flash (по договоренности, Flash-технологии решили в проекте не использовать);
  • с анимацией с помощью JQuery посредством функции $().animate (по причине ее прожорливости).

CANVAS в помощь!


Итак, остановились мы на использовании Canvas и JavaScript, посчитав этот вариант оптимальным для реализации нашей задачи.
Пофантазируем...
Любым доступным способом находим, рисуем или генерируем карту нашей будущей планеты. Предположим, у нас это будет экзопланета, где есть вода и растительность. Выглядеть карта нашей планеты будет примерно так:

image

Нам необходимо половину изображения скопировать и добавить с правой стороны, т.к. наша карта будет двигаться по поверхности canvas-блока, и при крайнем положении начинать движение заново. Результат получается таким:

image

image

Создаем планету
В качестве космического пространства у нас выступит блок div с любым подходящим для этого бэкграундом, а внутри разместим элемент с id=«planet»:

<div style="background-image:url(space.jpg); width:1000px; height: 1000px;">
     <canvas id="planet" width="300" height="300" style="position: absolute; left:200px; top: 200px;">
     </canvas>
</div>


Далее заставляем нашу карту двигаться внутри созданного элемента canvas, который скоро превратится в планету:

 $(function(){

        var pl_id = 'planet';

        var image = new Image();
        image.src = 'images/planets/1/1/map.jpg';

        // определяем длину и высоту элемента canvas
        var width = $('#'+pl_id).width();
        var height = $('#'+pl_id).height();

        var canvas = document.getElementById(pl_id);
        var id = canvas.getContext("2d");

        var newMoveWidth = 0;
        var newMoveHeight = 0;

        var drawPl = function(){

             id.clearRect(0, 0, width, height);
             // рисуем карту с новыми координатами внутри элемента
             id.drawImage(image, newMoveWidth, newMoveHeight, width, height, 0, 0, width, height); 

             if (newMoveWidth >= 899.5) newMoveWidth = 0; // если смещение достигло предела, начинаем сначала
             else newMoveWidth = newMoveWidth+0.5; // иначе двигаем карту дальше

        }

        setInterval(drawPl, 50); // запускаем анимацию со скоростью 50 мс.

 });


В результате произведенных действий, получаем примерно такую картину:

image

Закругляем...


Квадратных планет еще не открыли, поэтому придадим нашему небесному телу более привычный вид, прописав в style нашего элемента canvas border-radius на 50 процентов. Получаем:

image

Уже лучше, однако по-прежнему нет ощущения, что перед нами сферический объект.

Теперь подготовим в графическом редакторе круг с тенью по краям и бликами. Он должен быть обязательно полупрозрачным, т.к. мы будем накладывать его на нашу планетарную карту. В оригинале он будет выглядеть так:

image

Сейчас добавляем в нашу анимацию эффект вращения, а также, накладываем подготовленную картинку с тенями поверх карты планеты.

Финальный код нашей анимированной планеты получается таким:

<div style="background-image:url(space.jpg); width:1000px; height: 1000px;">
     <canvas id="planet" width="300" height="300" style="position: absolute; left:200px; top: 200px; border-radius:50%">   
     </canvas>
</div>


И код самой анимации:

$(function(){

        var pl_id = 'planet';

        var image = new Image();
        image.src = 'map2.jpg';

        // загружаем изображение тени и бликов планеты
        var fxShadow = new Image();
        fxShadow.src = 'planet_shadow.png';

        // определяем длину и высоту элемента canvas
        var width = $('#'+pl_id).width();
        var height = $('#'+pl_id).height();

        // рассчитываем угол вращения планеты
        var beta = 360/900;
        var beta = (beta*Math.PI)/360;
        var l = (Math.sqrt(width*width+width*width))/2;
        var gam = Math.PI - ((Math.PI - (beta * Math.PI)/360)/2) - (Math.PI/4);
        var b = 2*l*Math.sin(beta/2);
        var x = b*Math.sin(gam);
        var y = b*Math.cos(gam);
        var p1 = Math.cos(beta);
        var p2 = Math.sin(beta);

        var canvas = document.getElementById(pl_id);
        var id = canvas.getContext("2d");

        var newMoveWidth = 0;
        var newMoveHeight = 0;

        var drawPl = function(){

                id.clearRect(0, 0, width, height);
                // применяем к нашей планете вращение
                id.transform(p1, p2, -p2, p1, x, -y);
                // рисуем карту с новыми координатами внутри элемента
                id.drawImage(image, newMoveWidth, newMoveHeight, width, height, 0, 0, width, height); 
                //добавляем тень и блики
                id.drawImage(fxShadow, 0, 0, width, height);
                // если смещение достигло предела, начинаем сначала
                if (newMoveWidth >= 899.5) newMoveWidth = 0; 
                else newMoveWidth = newMoveWidth+0.5; // иначе двигаем карту дальше

        }

        setInterval(drawPl, 50); // запускаем анимацию со скоростью 50 мс.

 });


Финальный результат анимации планеты в игре:

image

image

Все вопросы и предложения по улучшению этого варианта реализации буду рад увидеть в комментариях.

Спасибо за внимание!

© Habrahabr.ru