Визуальная подсистема Kepler.gl построена поверх deck.gl, поэтому механизм эффектов наследует модель пост-обработки и сценических эффектов deck.gl. Эффекты в данном контексте представляют собой независимые модули, которые модифицируют итоговое изображение карты после рендеринга слоёв, не вмешиваясь в сами геометрические данные.
Базовая идея заключается в том, что эффекты подключаются к сцене как массив объектов, каждый из которых реализует жизненный цикл: инициализацию, обновление параметров и применение к кадру рендера.
Kepler.gl использует многоуровневый рендеринг:
Эффекты не изменяют данные слоёв напрямую. Вместо этого они работают с итоговым framebuffer, используя WebGL шейдеры или встроенные утилиты deck.gl.
В экосистеме deck.gl эффект обычно реализуется через наследование от
Effect:
import {Effect} from '@deck.gl/core';
class MyCustomEffect extends Effect {
constructor(props) {
super(props);
this.props = props;
}
initialize({gl}) {
// создание ресурсов WebGL
}
preDraw({gl, time}) {
// подготовка перед рендером сцены
}
draw({gl, width, height}) {
// пост-обработка кадра
}
cleanup() {
// освобождение ресурсов
}
}
Kepler.gl не ограничивает использование стандартных эффектов deck.gl, поэтому любой совместимый эффект может быть интегрирован в карту через конфигурацию.
Kepler.gl хранит конфигурацию визуализации в объекте mapState / mapStyle / visState. Эффекты передаются как часть состояния карты через API добавления данных.
Наиболее распространённый способ — использование
addDataToMap:
dispatch(
addDataToMap({
datasets: data,
options: {
centerMap: true,
readOnly: false
},
config: {
mapStyle: {
styleType: 'dark'
},
visState: {
effects: [
{
id: 'my-effect',
type: 'MyCustomEffect',
props: {
intensity: 0.5
}
}
]
}
}
})
);
В этом случае Kepler.gl интерпретирует объект эффекта как часть визуального состояния и передаёт его в слой deck.gl при инициализации сцены.
Чтобы Kepler.gl мог корректно создать экземпляр эффекта по строковому
идентификатору type, необходимо расширить фабрику
эффектов.
Типичный подход — создание реестра эффектов:
import {Effect} from '@deck.gl/core';
class HeatPulseEffect extends Effect {
initialize({gl}) {
this.gl = gl;
}
draw({gl, time}) {
// кастомная логика шейдера
}
}
const effectRegistry = {
HeatPulseEffect
};
export default effectRegistry;
Далее этот реестр интегрируется в слой инициализации Kepler.gl через расширение конфигурации приложения.
Kepler.gl активно использует Redux, поэтому регистрация эффектов часто выполняется на уровне store enhancer или middleware.
Пример расширения состояния:
const initialState = {
customEffects: {
HeatPulseEffect
}
};
И последующее использование при сборке карты:
const store = createStore(
rootReducer,
applyMiddleware(
keplerGlMiddleware({
customEffects: effectRegistry
})
)
);
Таким образом, при десериализации конфигурации Kepler.gl получает доступ к пользовательским эффектам.
Часть эффектов в Kepler.gl не требует полной реализации класса Effect. deck.gl предоставляет готовые пост-эффекты:
Они подключаются как готовые модули:
import {PostProcessEffect} from '@deck.gl/core';
import {BrightnessContrastEffect} from '@deck.gl/extensions';
const effects = [
new BrightnessContrastEffect({
contrast: 1.2,
brightness: 0.1
})
];
Kepler.gl может принимать такие эффекты напрямую, если они сериализуемы или создаются на этапе инициализации карты.
Каждый эффект может иметь динамические параметры, которые синхронизируются с состоянием приложения:
Пример структуры состояния:
visState: {
effects: [
{
id: 'pulse',
type: 'HeatPulseEffect',
props: {
speed: 1.5,
intensity: 0.8,
color: [255, 100, 50]
}
}
]
}
Изменение этих параметров через UI Kepler.gl приводит к пересозданию или обновлению эффекта без перезагрузки сцены.
Эффекты поддерживают реактивное обновление. При изменении состояния Kepler.gl выполняет diff конфигурации и вызывает обновление props у существующего эффекта.
Типовой механизм:
updateState(newProps) {
this.props = {
...this.props,
...newProps
};
}
Это позволяет реализовывать динамические визуализации, завязанные на временные ряды или пользовательский ввод.
При создании кастомного эффекта важно учитывать управление GPU-ресурсами:
cleanup;initialize.Пример:
cleanup() {
if (this.texture) {
this.texture.delete();
}
}
При регистрации кастомных эффектов необходимо учитывать:
Некорректная реализация эффекта может привести к блокировке render loop или утечкам GPU памяти.
Kepler.gl поддерживает стек эффектов, где каждый эффект применяется последовательно. Порядок в массиве имеет значение:
effects: [
new BloomEffect(),
new NoiseEffect(),
new ColorCorrectionEffect()
]
Эффекты могут взаимодействовать с кастомными слоями deck.gl, используемыми внутри Kepler.gl. В этом случае эффект может опираться на:
Это позволяет строить комплексные визуализации, где слой и эффект формируют единое поведение сцены.