В рамках большого интерактивного веб-ориентированного проекта (подробнее о котором возможно в другом посте) я занимаюсь разработкой картографического движка, реализованного на HTML5 CANVAS. Его разработка дошла до стадии беты и, с одобрения моего руководства, появилось желание продемонстрировать данные карты широкой публике.

http://uploads.ru/t/Z/T/G/ZTGWr.png

Общие сведения

Движок разрабатывался без использования каких-либо специализированных библиотек или фреймворков. Единственная используемая библиотека – jQuery.

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

Все отрисовывается на CANVAS’e, за исключением таких элементов как панель дополнительных инструментов и popup’ов меток (хотя в демо по ссылке ниже их все равно нет).

Реализация

Реализация модульная и состоит из следующих основных частей, назначение которых думаю понятно из их названий: CanvasDragger, CanvasEventer, CanvasImgLoader, CanvasMapper, CanvasMarker, CanvasMiniMapper, CanvasResizer, CanvasTools, CanvasZoomer.

Для того чтобы подключить карты достаточно в нужном месте html’a написать следующую строчку:

Код:
<canvas id="map2d"></canvas>

Далее в коде JS производим инициализацию (как пример):

Код:
$(function() {
    mWrap = new MapsWrapper({
        mapDivId: "map2d" // тут указываем ID canvas’a, в котором будет рисоваться карта
    });
});

MapsWrapper = function(properties) {
    this.initialize(properties);
};
$.extend(MapsWrapper.prototype, {
    v2DMapDiv                : null,
    v2DMapComponent   : null,

    initialize: function(prop){
        this.v2DMapDiv = prop.mapDivId;
        this.initMap();
    },

    initMap: function(){
        var GlobalParams = {
            staticMapUrl: ["http://gate.looxity.ru:8088/map.html", "http://zain.looxity.ru:8088/map.html", "http://kaph.looxity.ru:8088/map.html"],
            initCrd     : {x: 7445, y: 9925},
            initZoom    : 0.25,
            zoomList    : [1, 0.5, 0.25, 0.1, 0.05, 0.025],
            miniMap     : true,
            tools       : {scaler: true, polygoner: true}
        };
        this.v2DMapComponent = new CanvasMapper (this.v2DMapDiv);
        this.v2DMapComponent.initialize(GlobalParams);
    }
});

Остановимся поподробнее на параметрах:
staticMapUrl – хосты, с которых подгружаются тайлы карты
initCrd – начальные координаты в проекции Гаусса-Крюгера, в данном случае примерно соответствуют нулевому километру автодорог, что рядом с Манежной площадью.
miniMap – подключение модуля миникарты
tools – подключение модуля дополнительных инструментов

Внутренняя механика

Или что скрывается за тем или иным действием пользователя. Пройдемся по основным событиям.

Стартуем

При инициализации карт рассчитывается количество тайлов, которое нужно показать, чтобы полностью покрыть canvas. Зная размеры canvas’a, при заданном размере тайлов в 256х256, проделываем данную операцию.

Двигаемся

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

Код:
unVisibleTilesCollector: function() {
        for(var cnt = 0; cnt < this.__TILES__.length; cnt++) {
            if( (this.__TILES__[cnt].canvX + this.tileSize) < 0
                || this.__TILES__[cnt].canvX > this.canvas.width
                || this.__TILES__[cnt].canvY > this.canvas.height
                || (this.__TILES__[cnt].canvY + this.tileSize) < 0
                ) {
                this.__TILES__.splice(cnt, 1);
                cnt--;
            }
        }
    }

Масштабируем (zoomIn, zoomOut)

При срабатывании события “mousewheel” последовательно происходят следующие основные действия:
копируется текущее положение всех тайлов
$.extend(this.__ANIM_TILES__, this.mapper.__TILES__)

средствами canvas’a и с помощью математики происходит уменьшение или увеличение тайлов (в зависимости от того как мы крутим колесико мыши) из копии, сделанной в пп1

Код:
for(cnt; cnt < this.animSteps; cnt++){
                setTimeout(function(){
                    _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height);
                   _this.ctxMarker.clearRect(0,0,_this.canvas.width,_this.canvas.height);
                    animScale += scale*stepScale;
                    _this.drawAllAnimTiles(evt, {
                        animScale: animScale,
                        stepCurrNum: Math.round(Math.abs(animScaleStart-animScale)/stepScale),
                        stepScale: stepScale
                    });
                 }, delay*cnt);
            }

сверху, по мере подгрузки, накладываются новые тайлы, в соответствии с новым масштабом
MapsWrapper.v2DMapComponent.update()

Работа в браузерах

Работа проверялась в FireFox, Chrome, Safari, Opera и IE последних версий.
Для тех кто все еще не в курсе лишний раз подчеркну следующее. Так как используется canvas, автоматически отпадают все браузеры, не поддерживающие данную технологию, а это — IE версии 8 и ниже и совсем уж старые версии вышеперечисленных браузеров.