Использование с vanilla JavaScript

Deck.gl представляет собой высокопроизводительную библиотеку визуализации пространственных данных, построенную поверх WebGL. Несмотря на широкую популярность интеграции с React, библиотека изначально проектировалась как независимый графический движок и может полноценно использоваться в обычных проектах на vanilla JavaScript без применения фреймворков.

Подход без React особенно востребован в следующих сценариях:

  • интеграция в существующие корпоративные веб-приложения;
  • использование внутри CMS и административных панелей;
  • внедрение в проекты на чистом JavaScript;
  • работа с собственными системами отображения карт;
  • создание автономных виджетов визуализации.

Основным объектом управления становится экземпляр класса Deck, который отвечает за создание WebGL-контекста, управление слоями, обработку событий и обновление сцены.


Подключение библиотеки

Наиболее простой вариант — использование CDN.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Deck.gl</title>

    <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>

    <style>
        body {
            margin: 0;
        }

        #container {
            width: 100vw;
            height: 100vh;
        }
    </style>
</head>
<body>

<div id="container"></div>

</body>
</html>

После подключения становится доступно глобальное пространство имён:

deck

Например:

const deckInstance = new deck.Deck({
    parent: document.getElementById('container')
});

Создание экземпляра Deck

Минимальная конфигурация включает контейнер и начальное состояние камеры.

const deckgl = new deck.Deck({
    parent: document.getElementById('container'),

    initialViewState: {
        longitude: 37.6176,
        latitude: 55.7558,
        zoom: 10,
        pitch: 0,
        bearing: 0
    },

    controller: true
});

Основные параметры

Параметр Назначение
parent DOM-контейнер
layers массив слоёв
initialViewState начальная камера
controller управление мышью
views настройка представлений
effects графические эффекты
onClick обработчик клика
onHover обработчик наведения

Добавление слоёв

Вся визуализация в Deck.gl строится вокруг концепции слоёв (Layers).

Каждый слой представляет определённый способ отображения данных:

  • точки;
  • линии;
  • полигоны;
  • тепловые карты;
  • кластеры;
  • текст;
  • иконки;
  • 3D-модели.

Слои передаются через параметр layers.

deckgl.setProps({
    layers: [
        layer1,
        layer2,
        layer3
    ]
});

ScatterplotLayer

Один из самых популярных слоёв — отображение точек.

Исходные данные

const points = [
    {
        position: [37.62, 55.75],
        size: 100,
        color: [255, 0, 0]
    },
    {
        position: [37.70, 55.80],
        size: 200,
        color: [0, 0, 255]
    }
];

Создание слоя

const scatterLayer = new deck.ScatterplotLayer({
    id: 'scatter',

    data: points,

    getPosition: d => d.position,

    getRadius: d => d.size,

    getFillColor: d => d.color,

    radiusMinPixels: 5,

    pickable: true
});

Добавление слоя:

deckgl.setProps({
    layers: [scatterLayer]
});

Настройка внешнего вида точек

Размер может задаваться несколькими способами.

Постоянный размер

getRadius: 50

Размер из данных

getRadius: d => d.population

Ограничение размеров

radiusMinPixels: 2,
radiusMaxPixels: 100

Цветовая настройка

Deck.gl использует формат RGBA.

[255, 0, 0]

Красный цвет.

[0, 255, 0]

Зелёный цвет.

[0, 0, 255]

Синий цвет.

[255, 0, 0, 120]

Полупрозрачный красный.

Динамическое вычисление цвета:

getFillColor: d => {
    if (d.value > 1000) {
        return [255, 0, 0];
    }

    return [0, 255, 0];
}

Обработка пользовательских событий

Наведение мыши

const scatterLayer = new deck.ScatterplotLayer({
    id: 'scatter',

    data,

    pickable: true,

    onHover: info => {
        console.log(info);
    }
});

Объект info содержит:

{
    object,
    x,
    y,
    coordinate,
    index,
    layer
}

Обработка клика

const scatterLayer = new deck.ScatterplotLayer({
    id: 'scatter',

    data,

    pickable: true,

    onClick: info => {
        console.log(info.object);
    }
});

Отображение всплывающих подсказок

Deck.gl не предоставляет встроенный компонент tooltip, однако позволяет создавать его через DOM.

const tooltip = document.createElement('div');

tooltip.style.position = 'absolute';
tooltip.style.display = 'none';

document.body.appendChild(tooltip);

Использование:

onHover: info => {

    if (!info.object) {
        tooltip.style.display = 'none';
        return;
    }

    tooltip.style.display = 'block';

    tooltip.style.left = `${info.x}px`;
    tooltip.style.top = `${info.y}px`;

    tooltip.textContent = info.object.name;
}

ArcLayer

Слой предназначен для отображения связей между двумя точками.

Данные

const routes = [
    {
        source: [37.62, 55.75],
        target: [30.31, 59.93]
    }
];

Создание слоя

const arcLayer = new deck.ArcLayer({
    id: 'arc',

    data: routes,

    getSourcePosition: d => d.source,

    getTargetPosition: d => d.target,

    getSourceColor: [0, 128, 255],

    getTargetColor: [255, 0, 0],

    getWidth: 5
});

LineLayer

Предназначен для отображения прямых линий.

const lineLayer = new deck.LineLayer({
    id: 'line',

    data: routes,

    getSourcePosition: d => d.source,

    getTargetPosition: d => d.target,

    getColor: [255, 140, 0],

    getWidth: 4
});

PolygonLayer

Используется для отображения областей и геометрий.

Пример данных

const polygons = [
    {
        contour: [
            [37.60, 55.70],
            [37.70, 55.70],
            [37.70, 55.80],
            [37.60, 55.80]
        ]
    }
];

Создание слоя

const polygonLayer = new deck.PolygonLayer({
    id: 'polygon',

    data: polygons,

    getPolygon: d => d.contour,

    getFillColor: [0, 128, 255, 120],

    getLineColor: [0, 0, 0],

    getLineWidth: 2,

    stroked: true,

    filled: true
});

GeoJsonLayer

Один из наиболее мощных слоёв библиотеки.

Позволяет визуализировать GeoJSON без дополнительной обработки.

GeoJSON

const geojson = {
    type: "FeatureCollection",
    features: [...]
};

Создание слоя

const geoJsonLayer = new deck.GeoJsonLayer({
    id: 'geojson',

    data: geojson,

    filled: true,

    stroked: true,

    pickable: true,

    getFillColor: [0, 150, 255, 100],

    getLineColor: [0, 0, 0]
});

Загрузка данных по сети

Часто данные поступают из API.

async function loadData() {

    const response = await fetch('/api/points');

    const data = await response.json();

    const layer = new deck.ScatterplotLayer({
        id: 'points',
        data,
        getPosition: d => d.coordinates
    });

    deckgl.setProps({
        layers: [layer]
    });
}

loadData();

Динамическое обновление слоёв

Deck.gl эффективно обновляет сцену при изменении свойств.

function updateData(newData) {

    const layer = new deck.ScatterplotLayer({
        id: 'points',
        data: newData,
        getPosition: d => d.position
    });

    deckgl.setProps({
        layers: [layer]
    });
}

Повторное создание слоя считается стандартной практикой в Deck.gl.


Использование нескольких слоёв

Обычно сцена состоит из нескольких визуализаций одновременно.

deckgl.setProps({
    layers: [
        polygonLayer,
        lineLayer,
        scatterLayer
    ]
});

Порядок имеет значение.

Слои отображаются сверху вниз:

layers: [
    backgroundLayer,
    roadsLayer,
    markersLayer
]

Последний слой располагается поверх предыдущих.


Работа с камерой

Текущее состояние камеры можно менять программно.

deckgl.setProps({
    viewState: {
        longitude: 37.6176,
        latitude: 55.7558,
        zoom: 12,
        pitch: 45,
        bearing: 30
    }
});

Анимация перемещения

deckgl.setProps({
    viewState: {
        longitude: 37.6176,
        latitude: 55.7558,
        zoom: 12,

        transitionDuration: 3000
    }
});

Использование MapView

По умолчанию Deck.gl работает через картографическое представление.

const view = new deck.MapView({
    repeat: true
});

Подключение:

const deckgl = new deck.Deck({
    views: [view]
});

Интеграция с MapLibre

Deck.gl часто используется поверх картографической подложки.

<link
    href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"
    rel="stylesheet">
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>

Создание карты:

const map = new maplibregl.Map({
    container: 'map',
    style: 'https://demotiles.maplibre.org/style.json',
    center: [37.6176, 55.7558],
    zoom: 10
});

Создание Deck.gl поверх карты:

const deckOverlay = new deck.MapboxOverlay({
    layers: [scatterLayer]
});

map.addControl(deckOverlay);

Такой подход обеспечивает синхронизацию карты и WebGL-визуализации.


Использование TextLayer

Для отображения подписей применяется TextLayer.

const textLayer = new deck.TextLayer({
    id: 'labels',

    data,

    getPosition: d => d.position,

    getText: d => d.name,

    getSize: 18,

    getColor: [0, 0, 0]
});

Использование IconLayer

Слой позволяет отображать пользовательские иконки.

const iconLayer = new deck.IconLayer({
    id: 'icons',

    data,

    pickable: true,

    iconAtlas: './icons.png',

    iconMapping: './icons.json',

    getIcon: d => 'marker',

    getPosition: d => d.position,

    getSize: 32
});

Использование HeatmapLayer

Предназначен для отображения плотности данных.

const heatmapLayer = new deck.HeatmapLayer({
    id: 'heatmap',

    data,

    getPosition: d => d.position,

    getWeight: d => d.value
});

Тепловые карты особенно эффективны при работе с десятками тысяч точек.


Производительность при работе с большими объёмами данных

Deck.gl создавался для обработки миллионов объектов.

Основные рекомендации:

Использование бинарных данных

Вместо сложных объектов:

[
    {
        lat: 55.7,
        lng: 37.6
    }
]

предпочтительно использовать:

Float32Array

или

Float64Array

Минимизация вычислений в аксессорах

Менее эффективно:

getRadius: d => expensiveCalculation(d)

Более эффективно:

data.forEach(item => {
    item.radius = expensiveCalculation(item);
});
getRadius: d => d.radius

Избегание лишних обновлений

Плохо:

setInterval(() => {
    deckgl.setProps({
        layers: createLayers()
    });
}, 50);

Лучше:

updateOnlyWhenDataChanged();

Уничтожение экземпляра

При удалении визуализации необходимо освобождать ресурсы WebGL.

deckgl.finalize();

Пример:

window.addEventListener('beforeunload', () => {
    deckgl.finalize();
});

После вызова метода:

  • освобождается память GPU;
  • удаляются обработчики событий;
  • уничтожается WebGL-контекст;
  • прекращаются циклы рендеринга.

Архитектура приложения на vanilla JavaScript

Для крупных проектов часто используется следующая структура:

src/
│
├── data/
│   └── api.js
│
├── layers/
│   ├── points.js
│   ├── polygons.js
│   └── routes.js
│
├── ui/
│   └── tooltip.js
│
├── deck/
│   └── deck-instance.js
│
└── main.js

Экземпляр Deck создаётся один раз:

export const deckgl = new deck.Deck({
    parent: document.getElementById('app'),
    controller: true
});

Каждый слой формируется отдельной функцией:

export function createPointLayer(data) {
    return new deck.ScatterplotLayer({
        id: 'points',
        data,
        getPosition: d => d.position
    });
}

Сборка сцены:

deckgl.setProps({
    layers: [
        createPolygonLayer(polygons),
        createLineLayer(routes),
        createPointLayer(points)
    ]
});

Подобная организация позволяет использовать Deck.gl в крупных приложениях на чистом JavaScript без React, сохраняя модульность, высокую производительность и полный контроль над жизненным циклом визуализации.