Ползунки и выпадающие списки через bind

Интерактивность в Vega и Vega-Lite строится вокруг сигналов (signals) и параметров (params). Одним из ключевых механизмов связи интерфейса и визуализации является свойство bind, позволяющее привязать HTML-элементы управления к данным графика.

Через bind реализуются:

  • ползунки (range);
  • выпадающие списки (select);
  • чекбоксы (checkbox);
  • текстовые поля (input);
  • переключатели (radio).

Наиболее востребованы именно ползунки и выпадающие списки, поскольку они позволяют:

  • фильтровать данные;
  • менять параметры визуализации;
  • динамически изменять масштаб;
  • переключать категории;
  • настраивать внешний вид графика без перерисовки страницы.

Архитектура интерактивности

В Vega-Lite пользовательский интерфейс обычно строится так:

  1. Создаётся параметр (param).

  2. Параметр связывается с HTML-элементом через bind.

  3. Значение параметра используется:

    • в фильтрации,
    • в вычислениях,
    • в кодировках (encoding),
    • в условиях (condition).

Схема работы:

HTML input -> bind -> param -> график

Ползунки (range)

Базовый синтаксис

Ползунок создаётся через bind: { input: "range" }.

Пример:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "values": [
      {"x": 1, "y": 10},
      {"x": 2, "y": 20},
      {"x": 3, "y": 30},
      {"x": 4, "y": 40}
    ]
  },

  "params": [
    {
      "name": "pointSize",
      "value": 100,
      "bind": {
        "input": "range",
        "min": 10,
        "max": 500,
        "step": 10
      }
    }
  ],

  "mark": "point",

  "encoding": {
    "x": {"field": "x", "type": "quantitative"},
    "y": {"field": "y", "type": "quantitative"},
    "size": {
      "value": {
        "expr": "pointSize"
      }
    }
  }
}

Параметры ползунка

min

Минимальное значение.

"min": 0

max

Максимальное значение.

"max": 100

step

Шаг изменения.

"step": 5

Если шаг не указан, браузер может использовать дробные значения.


name

Подпись рядом с ползунком.

"bind": {
  "input": "range",
  "name": "Размер: "
}

Изменение размеров элементов

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

Управление радиусом точек

{
  "params": [
    {
      "name": "radius",
      "value": 50,
      "bind": {
        "input": "range",
        "min": 10,
        "max": 300
      }
    }
  ],

  "mark": {
    "type": "circle"
  },

  "encoding": {
    "size": {
      "expr": "radius"
    }
  }
}

Управление прозрачностью

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

{
  "params": [
    {
      "name": "alpha",
      "value": 0.5,
      "bind": {
        "input": "range",
        "min": 0,
        "max": 1,
        "step": 0.1
      }
    }
  ],

  "mark": {
    "type": "bar",
    "opacity": {
      "expr": "alpha"
    }
  }
}

Динамическая фильтрация через ползунок

Фильтрация по значению

Ползунок может ограничивать отображаемые данные.

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "url": "data/cars.json"
  },

  "params": [
    {
      "name": "minHorsepower",
      "value": 100,

      "bind": {
        "input": "range",
        "min": 50,
        "max": 250,
        "step": 10,
        "name": "Минимум HP: "
      }
    }
  ],

  "transform": [
    {
      "filter": "datum.Horsepower >= minHorsepower"
    }
  ],

  "mark": "point",

  "encoding": {
    "x": {
      "field": "Horsepower",
      "type": "quantitative"
    },

    "y": {
      "field": "Miles_per_Gallon",
      "type": "quantitative"
    }
  }
}

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

Параметры через bind часто работают совместно с выражениями (expr).

Это позволяет:

  • выполнять вычисления;
  • комбинировать несколько параметров;
  • строить сложные интерактивные зависимости.

Пример:

"expr": "sliderValue * 2"

Несколько ползунков одновременно

Интерактивность становится особенно мощной при использовании нескольких параметров.

{
  "params": [
    {
      "name": "widthValue",
      "value": 2,

      "bind": {
        "input": "range",
        "min": 1,
        "max": 10
      }
    },

    {
      "name": "opacityValue",
      "value": 0.7,

      "bind": {
        "input": "range",
        "min": 0,
        "max": 1,
        "step": 0.1
      }
    }
  ]
}

Выпадающие списки (select)

Базовая структура

Выпадающий список создаётся через:

"bind": {
  "input": "select"
}

Список значений через options

Главный параметр — options.

{
  "params": [
    {
      "name": "selectedCountry",

      "value": "USA",

      "bind": {
        "input": "select",

        "options": [
          "USA",
          "Japan",
          "Germany"
        ]
      }
    }
  ]
}

Подпись списка

"bind": {
  "input": "select",
  "name": "Страна: "
}

Фильтрация через выпадающий список

Наиболее популярный сценарий — фильтрация категориальных данных.

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "url": "data/cars.json"
  },

  "params": [
    {
      "name": "originFilter",

      "value": "USA",

      "bind": {
        "input": "select",

        "options": [
          "USA",
          "Japan",
          "Europe"
        ],

        "name": "Регион: "
      }
    }
  ],

  "transform": [
    {
      "filter": "datum.Origin == originFilter"
    }
  ],

  "mark": "point",

  "encoding": {
    "x": {
      "field": "Horsepower",
      "type": "quantitative"
    },

    "y": {
      "field": "Miles_per_Gallon",
      "type": "quantitative"
    },

    "color": {
      "field": "Origin",
      "type": "nominal"
    }
  }
}

Списки с отображаемыми метками

Иногда требуется показать человеку одни значения, а использовать другие.

Для этого применяются:

  • options
  • labels
{
  "bind": {
    "input": "select",

    "options": [
      "usa",
      "jp",
      "eu"
    ],

    "labels": [
      "США",
      "Япония",
      "Европа"
    ]
  }
}

Переключение типа графика

Выпадающий список может менять тип визуализации.

Динамический mark

{
  "params": [
    {
      "name": "chartType",

      "value": "bar",

      "bind": {
        "input": "select",

        "options": [
          "bar",
          "line",
          "area"
        ]
      }
    }
  ],

  "mark": {
    "type": {
      "expr": "chartType"
    }
  }
}

Изменение цветовой схемы

{
  "params": [
    {
      "name": "scheme",

      "value": "blues",

      "bind": {
        "input": "select",

        "options": [
          "blues",
          "reds",
          "greens",
          "viridis"
        ]
      }
    }
  ],

  "encoding": {
    "color": {
      "field": "value",

      "scale": {
        "scheme": {
          "expr": "scheme"
        }
      }
    }
  }
}

Связка ползунка и списка

Интерфейс часто строится из нескольких связанных контролов.

{
  "params": [
    {
      "name": "category",

      "value": "A",

      "bind": {
        "input": "select",

        "options": ["A", "B", "C"]
      }
    },

    {
      "name": "limit",

      "value": 50,

      "bind": {
        "input": "range",
        "min": 0,
        "max": 100
      }
    }
  ]
}

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

В чистой Vega механизм строится на signals.

Базовый пример

{
  "$schema": "https://vega.github.io/schema/vega/v5.json",

  "signals": [
    {
      "name": "scaleFactor",

      "value": 1,

      "bind": {
        "input": "range",
        "min": 1,
        "max": 10,
        "step": 1
      }
    }
  ]
}

Отличия Vega и Vega-Lite

Vega-Lite

Использует:

  • params
  • декларативный стиль
  • минимальный объём кода

Подходит для:

  • аналитики;
  • BI;
  • быстрых интерфейсов;
  • дашбордов.

Vega

Использует:

  • signals
  • полный контроль над интерактивностью
  • низкоуровневое описание

Подходит для:

  • сложных UI;
  • нестандартных взаимодействий;
  • кастомной логики.

Кастомизация HTML-элементов

bind поддерживает дополнительные HTML-атрибуты.

Пример

"bind": {
  "input": "range",
  "min": 0,
  "max": 100,
  "step": 1,
  "name": "Значение: "
}

На стороне браузера это превращается в обычный:

<input type="range">

Динамическое масштабирование

Ползунки часто используются для управления доменом шкалы.

{
  "params": [
    {
      "name": "maxX",

      "value": 100,

      "bind": {
        "input": "range",
        "min": 10,
        "max": 500
      }
    }
  ],

  "encoding": {
    "x": {
      "field": "value",

      "scale": {
        "domain": [0, {"expr": "maxX"}]
      }
    }
  }
}

Условная логика

Параметры могут участвовать в условиях.

{
  "encoding": {
    "color": {
      "condition": {
        "test": "threshold > 50",
        "value": "red"
      },

      "value": "blue"
    }
  }
}

Работа с датами

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

{
  "params": [
    {
      "name": "year",

      "value": 2000,

      "bind": {
        "input": "range",
        "min": 1990,
        "max": 2020,
        "step": 1
      }
    }
  ]
}

Ограничения bind

1. Нет сложной HTML-разметки

bind создаёт только стандартные HTML-контролы.

Нельзя напрямую встроить:

  • таблицы;
  • кастомные React-компоненты;
  • сложные формы.

2. Ограниченный внешний вид

Стилизация зависит от браузера.

Для полноценного UI обычно используют:

  • React;
  • Vue;
  • Svelte;
  • внешние формы управления.

3. Ограниченная логика зависимостей

Сложные взаимосвязи между элементами удобнее реализовывать в Vega через signals.


Лучшие практики

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

Плохо:

"name": "x"

Хорошо:

"name": "minPrice"

Ограничение диапазонов

Слишком широкий диапазон ухудшает UX.

Плохо:

"min": 0,
"max": 1000000

Лучше:

"min": 0,
"max": 100

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

Без шага пользователь получает неудобные дробные значения.

"step": 1

Значения по умолчанию

Всегда желательно задавать value.

"value": 50

Типичные ошибки

Отсутствует value

Иногда график стартует с undefined.

{
  "name": "slider"
}

Правильно:

{
  "name": "slider",
  "value": 10
}

Неверный тип данных

Ползунок возвращает число, список — строку.

Ошибка:

datum.value > category

Неверный expr

Ошибка:

"expr": slider

Правильно:

"expr": "slider"

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

Значения параметров можно получать программно.

Vega View API

view.signal('threshold')

Изменение значения:

view.signal('threshold', 50).run()

Комбинирование с событиями

bind не исключает событийную модель Vega.

Можно одновременно использовать:

  • on;
  • signals;
  • params;
  • DOM-события;
  • hover/select-интерактивность.

Реальные сценарии использования

Аналитические панели

Ползунки:

  • диапазон цен;
  • годы;
  • размеры выборки.

Списки:

  • категория;
  • страна;
  • тип продукта.

Научная визуализация

Через bind удобно управлять:

  • коэффициентами;
  • параметрами моделей;
  • диапазонами измерений.

Образовательные приложения

Ползунки позволяют визуализировать:

  • изменение формул;
  • статистические параметры;
  • математические зависимости.

Сравнение bind и внешнего UI

bind

Преимущества:

  • минимум кода;
  • встроенная интерактивность;
  • декларативность.

Недостатки:

  • ограниченный UI;
  • слабая стилизация;
  • простые формы.

Внешний UI

Преимущества:

  • полный контроль;
  • сложные интерфейсы;
  • кастомный дизайн.

Недостатки:

  • больше кода;
  • синхронизация состояния;
  • необходимость интеграции.

Когда использовать bind

bind особенно эффективен, если требуется:

  • быстро добавить интерактивность;
  • создать прототип;
  • сделать аналитический график;
  • настроить фильтрацию;
  • реализовать простой UI внутри визуализации.

Для сложных приложений обычно комбинируют:

  • Vega/Vega-Lite;
  • React/Vue;
  • внешние контролы;
  • программное управление сигналами.