Система слоёв в Deck.gl построена вокруг расширяемой модели, в
которой каждый визуальный компонент инкапсулируется в класс, наследующий
базовый функционал фреймворка. Пользовательский слой представляет собой
класс, расширяющий Layer, и описывающий полный цикл
подготовки данных, создания GPU-ресурсов и отрисовки.
Ключевая идея заключается в разделении логики на несколько независимых этапов: подготовка атрибутов, управление состоянием, компиляция шейдеров и выполнение рендеринга через WebGL.
Любой кастомный слой создаётся через наследование от
Layer:
import {Layer} from '@deck.gl/core';
class CustomLayer extends Layer {
initializeState() {}
updateState({props, oldProps, changeFlags}) {}
draw({uniforms}) {}
}
Базовый класс предоставляет инфраструктуру:
Каждый слой проходит фиксированные стадии:
Вызывается один раз при создании слоя. Используется для:
AttributeManagerinitializeState() {
const attributeManager = this.getAttributeManager();
attributeManager.add({
positions: {size: 3, accessor: 'getPosition'},
colors: {size: 4, accessor: 'getColor'}
});
}
Вызывается при изменении props или данных.
updateState({props, oldProps, changeFlags}) {
if (changeFlags.dataChanged) {
this._updateGeometry();
}
}
Типичные сценарии:
Основная функция рендеринга. Получает униформы и вызывает WebGL пайплайн.
draw({uniforms}) {
const {gl} = this.context;
this.state.model.setUniforms({
...uniforms,
opacity: this.props.opacity
});
this.state.model.draw(gl);
}
Освобождение ресурсов:
Deck.gl использует AttributeManager для декларативного
описания атрибутов вершин.
Каждый атрибут описывает:
attributeManager.add({
positions: {
size: 3,
accessor: 'getPosition',
update: this.calculatePositions
}
});
Геометрия может формироваться динамически в
updateState:
_updateGeometry() {
const {data} = this.props;
const positions = [];
const colors = [];
for (const d of data) {
positions.push(...d.position);
colors.push(...d.color);
}
this.setState({positions, colors});
}
Deck.gl использует Model как обёртку над
WebGLProgram.
import {Model} from '@luma.gl/core';
this.state.model = new Model(gl, {
vs: vertexShader,
fs: fragmentShader,
attributes: {
positions: this.state.positionsBuffer
}
});
Модель включает:
Отвечает за позиционирование вершин:
attribute vec3 positions;
attribute vec4 colors;
uniform float opacity;
varying vec4 vColor;
void main(void) {
vColor = colors * opacity;
gl_Position = project_position_to_clipspace(positions, vec3(0.0), vec3(0.0));
}
Отвечает за пиксельную окраску:
precision highp float;
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
Deck.gl использует встроенные функции проекции:
project_position_to_clipspaceproject_scaleproject_normalЭти функции автоматически учитывают:
Instancing позволяет отрисовывать тысячи объектов одним draw call.
this.state.model = new Model(gl, {
isInstanced: true,
instanceCount: data.length
});
В шейдере:
attribute vec3 instancePositions;
void main(void) {
vec3 pos = positions + instancePositions;
gl_Position = project_position_to_clipspace(pos);
}
State слоя хранит:
this.setState({
model,
positionsBuffer,
colorsBuffer
});
Состояние отделено от props и не должно изменяться напрямую вне lifecycle методов.
Deck.gl автоматически сравнивает props:
updateState({changeFlags}) {
if (changeFlags.propsChanged) {
this._updateAttributes();
}
}
Переиспользование достигается через:
if (!this.state.model) {
this._createModel();
}
import {Deck} from '@deck.gl/core';
const deckgl = new Deck({
initialViewState: {
longitude: 0,
latitude: 0,
zoom: 3
},
layers: [
new CustomLayer({
data: dataset,
getPosition: d => d.position,
getColor: d => d.color
})
]
});
При работе с большими массивами данных применяются:
const positions = new Float32Array(data.length * 3);
AttributeManager поддерживает стратегии обновления:
neveralwaysonDirtypositions: {
size: 3,
update: 'onDirty'
}
Uniforms позволяют передавать глобальные параметры в шейдер:
this.state.model.setUniforms({
time: this.props.time,
opacity: this.props.opacity
});
В GLSL:
uniform float time;
uniform float opacity;
Кастомный слой может быть частью композиции:
renderLayers() {
return [
new CustomLayer({data}),
new ScatterplotLayer({data})
];
}
Один слой может содержать несколько моделей:
this.state = {
modelA,
modelB
};
draw() {
this.state.modelA.draw();
this.state.modelB.draw();
}
Типичные расширения:
Picking реализуется через pickingColors:
attribute vec3 instancePickingColors;
void main(void) {
picking_setPickingColor(instancePickingColors);
}
В React-экосистеме слой используется через DeckGL
компонент:
<DeckGL layers={[new CustomLayer({data})]} />
Слой остаётся независимым от UI-слоя и не содержит React-логики.