Spec-Zone.ru › D3.js 4

Изменения в D3 4.0

D3 4.0 является модульным. Вместо одной библиотеки, D3 теперь представляет собой множество небольших библиотек, предназначенных для совместной работы. Вы можете выбирать и использовать нужные части по своему усмотрению. Каждая библиотека поддерживается в собственном репозитории, что позволяет распределять ответственность и независимые циклы выпуска. Стандартный пакет объединяет около тридцати этих микробиблиотек.

<script src="https://d3js.org/d3.v4.js"></script>

Как и прежде, вы можете загружать дополнительные плагины поверх стандартного пакета, такие как масштабы ColorBrewer:

<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v0.3.js"></script>

Вам не обязательно использовать стандартный пакет! Если вы используете только d3-selection, используйте его как отдельную библиотеку. Как и стандартный пакет, вы можете загружать микробиблиотеки D3 с помощью обычных тегов script или RequireJS (отлично подходит для HTTP/2!):

<script src="https://d3js.org/d3-selection.v1.js"></script>

Вы также можете cat микробиблиотеки D3 в пользовательский пакет или использовать инструменты, такие как Webpack и Rollup, чтобы создать оптимизированные пакеты. Пользовательские пакеты отлично подходят для приложений, использующих подмножество функций D3; например, библиотека диаграмм React может использовать D3 для масштабирования и форм, а React — для манипулирования DOM. Микробиблиотеки D3 написаны как модули ES6, а Rollup позволяет выбирать символы на уровне символов для создания более компактных пакетов.

Маленькие файлы хороши, но модульность также делает D3 более интересной. Микробиблиотеки легче понять, разработать и протестировать. Они упрощают вовлечение и вклад новых людей. Они уменьшают различие между «ядерным модулем» и «плагином» и увеличивают темпы разработки функций D3.

Если вам не важна модульность, вы можете в основном проигнорировать это изменение и продолжать использовать стандартный пакет. Однако есть одно неизбежное следствие принятия модулей ES6: каждый символ в D3 4.0 теперь использует плоскую область имен вместо вложенной области имен D3 3.x. Например, d3.scale.linear теперь d3.scaleLinear, а d3.layout.treemap теперь d3.treemap. Принятие модулей ES6 также означает, что D3 теперь написана исключительно в строгом режиме и имеет лучшую читаемость. И было много других значительных улучшений функций D3! (Практически весь код из D3 3.x был переписан.) Эти изменения описаны ниже.

Другие глобальные изменения

Стандартный UMD-пакет теперь анонимный. Никакой d3 глобальной переменной не экспортируется, если обнаружен AMD или CommonJS. В обычной среде микробиблиотеки D3 используют d3 глобальную переменную, даже если вы загружаете их независимо; таким образом, ваш код одинаков, независимо от того, используете ли вы стандартный пакет или нет. (См. Создадим (D3) плагин для получения дополнительной информации.) Сгенерированный пакет больше не хранится в репозитории Git; Bower был перенаправлен на d3-bower, а сгенерированные файлы можно найти на npm или в прикреплённом последнем релизе. Стандартный неминифицированный пакет больше не искажается, что делает его более читаемым и сохраняет встроенные комментарии.

К сожалению некоторых пользователей, 3.x использовал символы Юникода, такие как λ, φ, τ и π, для краткого представления математических операций. Недостатком этого подхода было то, что ошибка SyntaxError возникала при загрузке неминифицированного D3 с помощью ISO-8859-1 вместо UTF-8. 3.x также использовал литералы строк Юникода, такие как SI-префикс µ для 1e-6. 4.0 использует только переменные ASCII и литералы строк ASCII (см. rollup-plugin-ascii), избегая проблем с кодировкой.

Оглавление

  • Массивы
  • Оси
  • Щётки
  • Хорды
  • Коллекции
  • Цвета
  • Диспечирование
  • Перетаскивание
  • Разделенные значения
  • Сглаживания
  • Силы
  • Форматы чисел
  • Географические данные
  • Иерархии
  • Внутренние компоненты
  • Интерполяторы
  • Пути
  • Многоугольники
  • Четвертичные деревья
  • Очереди
  • Случайные числа
  • Запросы
  • Масштабирование
  • Выделения
  • Формы
  • Форматы времени
  • Интервалы времени
  • Таймеры
  • Переходы
  • Диаграммы Вороного
  • Масштабирование

Массивы (d3-array)

Новый метод d3.scan выполняет линейный просмотр массива, возвращая индекс наименьшего элемента в соответствии с указанным компаратором. Это аналогично d3.min и d3.max, за исключением того, что вы можете использовать его для поиска позиции крайнего элемента, а не просто вычислять крайнее значение.

var data = [
  {name: "Alice", value: 2},
  {name: "Bob", value: 3},
  {name: "Carol", value: 1},
  {name: "Dwayne", value: 5}
];

var i = d3.scan(data, function(a, b) { return a.value - b.value; }); // 2
data[i]; // {name: "Carol", value: 1}

Новые методы d3.ticks и d3.tickStep полезны для генерации численных делений, понятных человеку. Эти методы являются альтернативой низкого уровня для continuous.ticks из d3-scale. Новая реализация также более точна, возвращая оптимальное количество делений, измеренное относительной погрешностью.

var ticks = d3.ticks(0, 10, 5); // [0, 2, 4, 6, 8, 10]

Метод d3.range больше не делает сложных попыток избежать ошибки с плавающей точкой, когда шаг не является целым числом. Возвращаемые значения строго определяются как начало + i * шаг, где i — целое число. (Узнайте больше о математике с плавающей точкой.) d3.range возвращает пустой массив для бесконечных диапазонов, а не генерирует ошибку.

Подпись метода для необязательных аксессоров была изменена для большей согласованности с методами массивов, такими как array.forEach: аксессор получает текущий элемент (d), индекс (i) и массив (данные), при этом this равно undefined. Это влияет на d3.min, d3.max, d3.extent, d3.sum, d3.mean, d3.median, d3.quantile, d3.variance и d3.deviation. Метод d3.quantile ранее не принимал аксессор. Некоторые методы с необязательными аргументами теперь рассматривают эти аргументы как отсутствующие, если они равны null или undefined, а не проверяют строго arguments.length.

Новый API d3.histogram заменяет d3.layout.histogram. Вместо того чтобы экспонировать bin.x и bin.dx для каждого возвращённого бина, гистограмма экспонирует bin.x0 и bin.x1, гарантируя, что bin.x0 точно равно bin.x1 в предыдущем бине. Режимы «частота» и «вероятность» больше не поддерживаются; каждый бином — просто массив элементов из входных данных, поэтому bin.length равен bin.y в режиме частоты в D3 3.x. Чтобы вычислить распределение вероятностей, разделите количество элементов в каждом бине на общее количество элементов.

Метод histogram.range был переименован в histogram.domain для согласованности со шкалами. Метод histogram.bins был переименован в histogram.thresholds и больше не принимает верхнее значение: n порогов создадут n + 1 бины. Если вы указываете желаемое количество бинов вместо порогов, d3.histogram теперь использует d3.ticks для вычисления хороших порогов бинов. Помимо стандартной формулы Стерджеса, D3 теперь реализует правило Фридемана-Диакониса и правило Скотта для нормальной выборки.

Оси (d3-axis)

Чтобы правильно отобразить оси в D3 3.x, вам нужно было стилизовать их:

<style>

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.axis text {
  font: 10px sans-serif;
}

</style>
<script>

d3.select(".axis")
    .call(d3.svg.axis()
        .scale(x)
        .orient("bottom"));

</script>

Если вы этого не делали, вы видели это:

D3 4.0 предоставляет стандартные стили и более короткий синтаксис. Вместо d3.svg.axis и axis.orient, D3 4.0 теперь предоставляет четыре конструктора для каждой ориентации: d3.axisTop, d3.axisRight, d3.axisBottom, d3.axisLeft. Эти конструкторы принимают шкалу, поэтому вы можете свести всё вышесказанное к:

<script>

d3.select(".axis")
    .call(d3.axisBottom(x));

</script>

И получить это:

Как и прежде, вы можете настроить внешний вид оси, применяя таблицы стилей или изменяя элементы оси. Стандартный внешний вид был незначительно изменён, чтобы сместить ось на половину пикселя; это исправляет проблему с отображением чётких краёв в Safari, где ось отображалась толщиной в два пикселя.

Теперь есть метод axis.tickArguments, как альтернатива axis.ticks, который также позволяет просматривать аргументы делений оси. Метод axis.tickSize был изменён так, чтобы принимать только один аргумент при установке размера делений. Методы axis.innerTickSize и axis.outerTickSize были переименованы в axis.tickSizeInner и axis.tickSizeOuter соответственно.

Щётки (d3-brush)

Заменяя d3.svg.brush, теперь доступны три класса кисти для выделения по оси x, оси y или обеим осям: d3.brushX, d3.brushY, d3.brush. Кисти больше не зависят от масштабов; вместо этого каждая кисть определяет выделение в координатах экрана. Это выделение может быть инвертировано, если вы хотите вычислить соответствующую область значений данных. Вместо того, чтобы полагаться на диапазоны масштабов для определения области выделения, теперь есть метод brush.extent для её установки. Если вы не устанавливаете область выделения, она по умолчанию равна всей области элемента SVG. Метод brush.clamp также был устранён; выделение всегда ограничено областью, определённой методом brush.extent.

Кисти больше не хранят активное выделение (т.е., выделенную область; положение кисти) внутри себя. Положение кисти теперь хранится в любых элементах, к которым применена кисть. Положение кисти доступно как event.selection в событии кисти или вызовом d3.brushSelection на данном элементе. Для программного перемещения кисти используйте brush.move с заданным выделением или переходом; см. пример привязки кисти. Метод brush.event был удалён.

Взаимодействие с кистью было улучшено. По умолчанию кисти теперь игнорируют правые щелчки мыши, предназначенные для контекстного меню; вы можете изменить это поведение, используя brush.filter. Кисти также игнорируют эмулированные события мыши на iOS. Удерживая клавишу SHIFT (⇧) при выделении, вы фиксируете положение кисти по оси x или y. Удерживание клавиши META (⌘) при щелчке и перетаскивании инициирует новое выделение, а не перемещение существующего.

По умолчанию внешний вид кисти также был улучшен и немного упрощён. Ранее требовалось применять стили к кисти, чтобы она имела приемлемый вид, например:

.brush .extent {
  stroke: #fff;
  fill-opacity: .125;
  shape-rendering: crispEdges;
}

Теперь эти стили применяются по умолчанию как атрибуты; если вы хотите настроить внешний вид кисти, вы по-прежнему можете применять внешние стили или изменять элементы кисти. (В D3 4.0 аналогичное улучшение применено к осям.) Новый метод brush.handleSize позволяет переопределить размер ручки кисти; по умолчанию он равен шести пикселям.

Кисть теперь потребляет обработанные события, что облегчает её объединение с другими интерактивными действиями, такими как перетаскивание и увеличение/уменьшение. События brushstart и brushend были переименованы соответственно в start и end. Событие кисти больше не сообщает о event.mode для различения изменения размера и перетаскивания кисти.

Хорды (d3-chord)

В соответствии с большим сглаживанием пространства имён:

  • d3.layout.chord ↦ d3.chord
  • d3.svg.chord ↦ d3.ribbon

Для согласованности с arc.padAngle, chord.padding также был переименован в ribbon.padAngle. Новый метод ribbon.context позволяет отображать диаграммы хорд в Canvas! Также см. d3-path.

Коллекции (d3-collection)

Конструктор d3.set теперь принимает существующее множество для создания копии. Если вы передаёте массив в d3.set, вы также можете передать функцию-аксессор значения. Этот аксессор принимает стандартные аргументы: текущий элемент (d), индекс (i) и массив (data), при этом this неопределён. Например:

var yields = [
  {yield: 22.13333, variety: "Manchuria",        year: 1932, site: "Grand Rapids"},
  {yield: 26.76667, variety: "Peatland",         year: 1932, site: "Grand Rapids"},
  {yield: 28.10000, variety: "No. 462",          year: 1931, site: "Duluth"},
  {yield: 38.50000, variety: "Svansota",         year: 1932, site: "Waseca"},
  {yield: 40.46667, variety: "Svansota",         year: 1931, site: "Crookston"},
  {yield: 36.03333, variety: "Peatland",         year: 1932, site: "Waseca"},
  {yield: 34.46667, variety: "Wisconsin No. 38", year: 1931, site: "Grand Rapids"}
];

var sites = d3.set(yields, function(d) { return d.site; }); // Grand Rapids, Duluth, Waseca, Crookston

Конструктор d3.map также следует стандартной схеме аргументов-аксессоров массива.

Методы map.forEach и set.forEach были переименованы соответственно в map.each и set.each. Порядок аргументов для map.each также был изменён на value, key и map, а порядок аргументов для set.each теперь value, value и set. Это ближе к ES6 map.forEach и set.forEach. Также, как и в ES6 Map и Set, map.set и set.add теперь возвращают текущую коллекцию (а не добавленное значение), что облегчает цепочку методов. Новые методы map.clear и set.clear могут использоваться для очистки коллекций.

Метод nest.map теперь всегда возвращает экземпляр d3.map. Для простого объекта используйте nest.object вместо этого. При использовании совместно с nest.rollup, nest.entries теперь возвращает объекты {key, value} для листовых элементов, а не {key, values}. Это делает nest.rollup проще в использовании совместно с иерархиями, как в этом примере древовидной карты.

Цвета (d3-color)

Теперь все цвета имеют непрозрачность, доступную как color.opacity, которая является числом в диапазоне [0, 1]. Вы можете передать необязательный аргумент непрозрачности в конструкторы цветовых пространств d3.rgb, d3.hsl, d3.lab, d3.hcl или d3.cubehelix.

Теперь вы можете анализировать цветовые спецификаторы CSS rgba(…) и hsla(…) или строку “transparent” с помощью d3.color. Цвет “transparent” определяется как RGB цвет с нулевой непрозрачностью и неопределёнными каналами красного, зелёного и синего; это немного отличается от CSS, который определяет его как прозрачный чёрный, но полезно для упрощения логики интерполяции цветов, где начальный или конечный цвет имеет неопределённые каналы. Метод color.toString теперь аналогичным образом возвращает строку rgb(…) или rgba(…) с целочисленными значениями каналов, а не шестнадцатеричным RGB форматом, в соответствии с вычисленными значениями CSS. Это повышает производительность, позволяя прерывать переходы, когда начальный стиль элемента совпадает с конечным стилем.

Новый метод d3.color является основным методом для анализа цветов: он возвращает экземпляр d3.color в соответствующем цветовом пространстве или null, если спецификатор цвета CSS некорректен. Например:

var red = d3.color("hsl(0, 80%, 50%)"); // {h: 0, l: 0.5, s: 0.8, opacity: 1}

Реализация анализа теперь более надёжна. Например, теперь нельзя смешивать целые числа и проценты в rgb(…), и она корректно обрабатывает пробелы, десятичные точки, знаки, и другие граничные случаи. Конструкторы цветовых пространств d3.rgb, d3.hsl, d3.lab, d3.hcl и d3.cubehelix теперь всегда возвращают копию входного цвета, преобразованного в соответствующее цветовое пространство. Хотя color.rgb остаётся, rgb.hsl был удалён; используйте d3.hsl для преобразования цвета в цветовое пространство RGB.

Цветовое пространство RGB больше не жадно квантует и не ограничивает значения каналов при создании цветов, что улучшает точность при преобразовании в цветовом пространстве. Квантование и ограничение теперь происходит в color.toString при форматировании цвета для отображения. Вы можете использовать новый метод color.displayable для проверки, является ли цвет вне гаммы.

Метод rgb.brighter больше не обрабатывает чёрный цвет особо. Это мультипликативный оператор, определяющий новый цвет r′, g′, b′, где r′ = r × pow(0.7, k), g′ = g × pow(0.7, k) и b′ = b × pow(0.7, k); более светлый чёрный по-прежнему остаётся чёрным.

Есть новое цветовое пространство d3.cubehelix, обобщающее цветовую схему Дэвида Грина! (См. также d3.interpolateCubehelixDefault из d3-scale.) Вы также можете продолжать определять свои собственные настраиваемые цветовые пространства; см. d3-hsv для примера.

Диспечеры (d3-dispatch)

Вместо декорации объекта dispatch каждым типом события, объект диспечера теперь предоставляет универсальные методы dispatch.call и dispatch.apply, которые принимают строку type в качестве первого аргумента. Например, в D3 3.x вы могли бы сказать:

dispatcher.foo.call(that, "Hello, Foo!");

Чтобы отправить событие foo в D3 4.0, вы бы сказали:

dispatcher.call("foo", that, "Hello, Foo!");

Метод dispatch.on теперь принимает несколько имен типов, что позволяет добавлять или удалять слушателей для нескольких событий одновременно. Например, чтобы отправить события foo и bar одному и тому же слушателю:

dispatcher.on("foo bar", function(message) {
  console.log(message);
});

Это соответствует новому поведению selection.on в d3-selection. Метод dispatch.on теперь проверяет, что указатель listener является функцией, а не выбросит ошибку в будущем.

Новая реализация d3.dispatch работает быстрее, используя меньше замыканий для повышения производительности. Также есть новый метод dispatch.copy для создания копии диспечера; он используется в d3-transition для повышения производительности переходов в распространённом случае, когда у всех элементов перехода одинаковые слушатели событий перехода.

Перетаскивание (d3-drag)

Поведение перетаскивания d3.behavior.drag переименовано в d3.drag. Метод drag.origin заменен на drag.subject, который позволяет определить объект, который перетаскивается в начале жеста перетаскивания. Это особенно полезно с Canvas, где перетаскиваемые объекты обычно используют один элемент Canvas (в отличие от SVG, где перетаскиваемые объекты обычно имеют отдельные DOM-элементы); см. пример перетаскивания круга здесь.

Новый метод drag.container позволяет переопределить родительский элемент, определяющий систему координат жеста перетаскивания. По умолчанию используется родительский узел элемента, к которому было применено поведение перетаскивания. Для перетаскивания на элементах Canvas вы, вероятно, захотите использовать элемент Canvas в качестве контейнера.

События перетаскивания теперь предоставляют метод event.on для регистрации временных слушателей на период текущего жеста перетаскивания; эти слушатели могут захватывать состояние текущего жеста, например, объект, который перетаскивается. Новое свойство event.active позволяет определить, активны ли одновременно несколько жестов перетаскивания (мультитач). События dragstart и dragend переименованы в start и end. По умолчанию поведение перетаскивания игнорирует щелчки правой кнопкой мыши, предназначенные для контекстного меню; используйте drag.filter для управления событиями, которые будут игнорироваться. Поведение перетаскивания также игнорирует эмулированные события мыши на iOS. Поведение перетаскивания теперь потребляет обработанные события, что упрощает его комбинирование с другими интерактивными действиями, такими как приближение.

Новые методы d3.dragEnable и d3.dragDisable предоставляют низкоуровневый API для реализации жестов перетаскивания в разных браузерах и на различных устройствах. Эти методы также используются другими компонентами D3, например, щеткой.

Разделители (d3-dsv)

В соответствии с объединением пространств имен различные методы CSV и TSV получили новые имена:

  • d3.csv.parse ↦ d3.csvParse
  • d3.csv.parseRows ↦ d3.csvParseRows
  • d3.csv.format ↦ d3.csvFormat
  • d3.csv.formatRows ↦ d3.csvFormatRows
  • d3.tsv.parse ↦ d3.tsvParse
  • d3.tsv.parseRows ↦ d3.tsvParseRows
  • d3.tsv.format ↦ d3.tsvFormat
  • d3.tsv.formatRows ↦ d3.tsvFormatRows

Методы d3.csv и d3.tsv для загрузки файлов соответствующих форматов не переименованы! Они определены в d3-request. Больше нет метода d3.dsv, который выполнял тройную задачу по определению форматировщика DSV, парсера DSV и запросчика DSV; вместо этого есть только d3.dsvFormat, который вы можете использовать для определения форматировщика и парсера DSV. Вы можете использовать request.response для выполнения запроса и последующего анализа тела ответа, или просто использовать d3.text.

Метод dsv.parse теперь показывает имена столбцов и их порядок ввода как data.columns. Например:

d3.csv("cars.csv", function(error, data) {
  if (error) throw error;
  console.log(data.columns); // ["Year", "Make", "Model", "Length"]
});

Аналогичным образом, вы можете передать необязательный массив имён столбцов в dsv.format, чтобы отформатировать только подмножество столбцов или явно указать порядок столбцов:

var string = d3.csvFormat(data, ["Year", "Model", "Length"]);

Парсер немного быстрее, а форматировщик немного надёжнее: входные данные приводятся к строкам перед форматированием, устраняя неочевидную ошибку, и удалена устаревшая поддержка обратного вызова dsv.formatRows в случае, если входные данные data являются массивом массивов.

Сплайны (d3-ease)

D3 3.x использовала строки, такие как «кубический-в-наружу», для идентификации методов сплайнов; эти строки могли быть переданы в d3.ease или transition.ease. D3 4.0 использует символы вместо этого, такие как d3.easeCubicInOut. Символы проще и чище. Они хорошо работают с Rollup для создания более компактных пользовательских пакетов. Если необходимо, вы всё ещё можете определить собственную функцию сплайна.

Вот полный список эквивалентов:

  • linear ↦ d3.easeLinear¹
  • linear-in ↦ d3.easeLinear¹
  • linear-out ↦ d3.easeLinear¹
  • linear-in-out ↦ d3.easeLinear¹
  • linear-out-in ↦ d3.easeLinear¹
  • poly-in ↦ d3.easePolyIn
  • poly-out ↦ d3.easePolyOut
  • poly-in-out ↦ d3.easePolyInOut
  • poly-out-in ↦ УДАЛЕНО²
  • и т. д.

¹ Варианты -in, -out и -in-out для линейного сплайна идентичны, поэтому существует только d3.easeLinear.
² Сплайны упругости и отскока были непреднамеренно перепутаны в 3.x, поэтому в 4.0 удалены -out-in сплайны!

Для удобства также существуют псевдонимы для каждого метода сплайна. Например, d3.easeCubic — псевдоним для d3.easeCubicInOut. Большинство псевдонимов по умолчанию соответствуют -in-out; исключениями являются d3.easeBounce и d3.easeElastic, у которых по умолчанию -out.

Вместо передачи необязательных аргументов в d3.ease или transition.ease, параметризуемые функции сплайна теперь имеют именованные параметры: poly.exponent, elastic.amplitude, elastic.period и back.overshoot.

И т. д.

Многие функции сплайна были оптимизированы для производительности и точности. Также были исправлены несколько ошибок, такие как интерпретация параметра перескока для сплайна обратного хода и параметра периода для упругого сплайна. Кроме того, d3-transition теперь гарантирует, что последний тик перехода происходит ровно при t = 1, что исключает ошибки с плавающей точкой в некоторых функциях сплайна.

Теперь есть удобная справочная таблица и анимированная таблица новых функций сплайна.

Силы (d3-force)

Расположение сил d3.layout.force переименовано в d3.forceSimulation. Симуляция сил теперь использует интеграцию скорости Верлета вместо интеграции позиции Верлета, отслеживая позиции узлов (node.x, node.y) и скорости (node.vx, node.vy) вместо их предыдущих позиций (node.px, node.py).

Вместо жёсткого кодирования набора встроенных сил симуляция сил теперь расширяема: вы указываете необходимые вам силы! Такой подход обеспечивает большую гибкость благодаря композиции. Новые силы также более гибкие: параметры сил обычно можно настраивать для каждого узла или каждой связи. Существуют отдельные силы позиционирования для x и y, которые заменяют force.gravity; x.x и y.y заменяют force.size. Новая сила связи заменяет force.linkStrength и использует лучшие эвристические методы для повышения стабильности. Новая многоточечная сила заменяет force.charge и поддерживает новый параметр минимального расстояния, а также улучшения производительности благодаря новым деревьям квадро-разбиения 4.0. Также есть совершенно новые силы для центрирования узлов и устранения столкновений.

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

Phyllotaxis

Случайные колебания по-прежнему необходимы для разрешения связей, столкновений и сил многочастичных взаимодействий, если существуют совпадающие узлы, но по крайней мере в общем случае, симуляция сил (и результирующая компоновка графа с направленными силами) теперь согласована между браузерами и перезагрузками. D3 больше не играет в кости!

Симуляция сил имеет несколько новых методов для большего контроля над нагревом, таких как simulation.alphaMin и simulation.alphaDecay, а также внутренний таймер. Вызов simulation.alpha теперь не оказывает влияния на внутренний таймер, который контролируется независимо с помощью simulation.stop и simulation.restart. Внутренний таймер компоновки сил теперь автоматически запускается при создании, удаляя force.start. Как и в 3.x, вы можете вручную продвигать симуляцию с помощью simulation.tick. Параметр force.friction заменён на simulation.velocityDecay. Новый метод simulation.alphaTarget позволяет установить желаемую альфу (температуру) симуляции таким образом, чтобы симуляция могла плавно нагреваться во время взаимодействия, а затем плавно охлаждаться снова. Это улучшает стабильность графа во время взаимодействия.

Компоновка сил больше не зависит от поведения перетаскивания, хотя вы можете, конечно, создать перетаскиваемые графы с направленными силами! Установите node.fx и node.fy, чтобы зафиксировать положение узла. В качестве альтернативы наложению SVG Вороного теперь можно использовать simulation.find для поиска ближайшего узла к указателю.

Форматы чисел (d3-format)

Если точность не указана, поведение форматирования изменилось: теперь существует значение по умолчанию точности 6 для всех директив, кроме none, для которой значение по умолчанию равно 12. В 3.x, если вы не указывали точность, число форматировалось с использованием его кратчайшего уникального представления (по number.toString); это могло привести к неожиданным цифрам из-за плавающей арифметики с плавающей запятой. Новая точность по умолчанию в 4.0 обеспечивает более согласованные результаты:

var f = d3.format("e");
f(42);        // "4.200000e+1"
f(0.1 + 0.2); // "3.000000e-1"

Для обрезки несущественных конечных нулей используйте директиву none, которая похожа g. Например:

var f = d3.format(".3");
f(0.12345);   // "0.123"
f(0.10000);   // "0.1"
f(0.1 + 0.2); // "0.3"

Внутри, форматирование чисел улучшило точность с очень большими и очень маленькими числами, используя number.toExponential вместо Math.log для извлечения мантиссы и показателя степени. Отрицательный ноль (-0, конструкция IEEE 754) и очень маленькие числа, которые округляются до нуля, теперь форматируются как беззнаковый ноль. Встроенный небезопасный метод d3.round удалён, как и d3.requote.

Метод d3.formatPrefix изменён. Вместо возврата строки с префиксом СИ, он возвращает функцию форматирования с префиксом СИ для заданного specifier и эталонного value. Например, для форматирования тысяч:

var f = d3.formatPrefix(",.0", 1e3);
f(1e3); // "1k"
f(1e4); // "10k"
f(1e5); // "100k"
f(1e6); // "1,000k"

В отличие от s директивы форматирования, d3.formatPrefix всегда использует тот же префикс СИ, что обеспечивает согласованные результаты:

var f = d3.format(".0s");
f(1e3); // "1k"
f(1e4); // "10k"
f(1e5); // "100k"
f(1e6); // "1M"

Новый ( параметр знака использует скобки для отрицательных значений. Это особенно полезно в сочетании с $. Например:

d3.format("+.0f")(-42);  // "-42"
d3.format("(.0f")(-42);  // "(42)"
d3.format("+$.0f")(-42); // "-$42"
d3.format("($.0f")(-42); // "($42)"

Новый = параметр выравнивания размещает любой знак и символ слева от любого заполнения:

d3.format(">6d")(-42);  // "   -42"
d3.format("=6d")(-42);  // "-   42"
d3.format(">(6d")(-42); // "  (42)"
d3.format("=(6d")(-42); // "(  42)"

b, o, d и x директивы теперь округляют до ближайшего целого числа, а не возвращают пустую строку для нецелых чисел:

d3.format("b")(41.9); // "101010"
d3.format("o")(41.9); // "52"
d3.format("d")(41.9); // "42"
d3.format("x")(41.9); // "2a"

Директива c теперь предназначена для символьных данных (т.е., литеральных строк), а не для кодов символов. Это полезно, если вам нужно только применить выравнивание и заполнение, и вы не беспокоитесь о форматировании чисел. Например, печально известное left-pad (а также center- и right-pad!) может быть удобно реализован как:

d3.format(">10c")("foo"); // "       foo"
d3.format("^10c")("foo"); // "   foo    "
d3.format("<10c")("foo"); // "foo       "

Существует несколько новых методов для вычисления рекомендуемых десятичных точностей; они используются d3-scale для форматирования штрихов и полезны для реализации пользовательских числовых форматов: d3.precisionFixed, d3.precisionPrefix и d3.precisionRound. Также есть новый метод d3.formatSpecifier для разбора, проверки и отладки спецификаторов форматов; он также хорош для вывода связанных спецификаторов форматов, например, когда вы хотите автоматически подставить точность.

Теперь вы можете установить язык по умолчанию, используя d3.formatDefaultLocale! Языковые наборы опубликованы как JSON в npm.

Географические данные (d3-geo)

В соответствии с большим сглаживанием пространства имён, различные методы получили новые имена:

  • d3.geo.graticule ↦ d3.geoGraticule
  • d3.geo.circle ↦ d3.geoCircle
  • d3.geo.area ↦ d3.geoArea
  • d3.geo.bounds ↦ d3.geoBounds
  • d3.geo.centroid ↦ d3.geoCentroid
  • d3.geo.distance ↦ d3.geoDistance
  • d3.geo.interpolate ↦ d3.geoInterpolate
  • d3.geo.length ↦ d3.geoLength
  • d3.geo.rotation ↦ d3.geoRotation
  • d3.geo.stream ↦ d3.geoStream
  • d3.geo.path ↦ d3.geoPath
  • d3.geo.projection ↦ d3.geoProjection
  • d3.geo.projectionMutator ↦ d3.geoProjectionMutator
  • d3.geo.albers ↦ d3.geoAlbers
  • d3.geo.albersUsa ↦ d3.geoAlbersUsa
  • … (и так далее)

Также переименованы для согласованности:

  • circle.origin ↦ circle.center
  • circle.angle ↦ circle.radius
  • … (и так далее)

Проекции теперь имеют более подходящие значения по умолчанию. Например, d3.geoOrthographic по умолчанию имеет угол обрезки 90°, показывая только переднюю полусферу, а d3.geoGnomonic имеет угол обрезки по умолчанию 60°. Проекция по умолчанию projection для d3.geoPath теперь равна null, а не d3.geoAlbersUsa; нулевая проекция используется с заранее спроецированной геометрией и, как правило, быстрее отрисовывается.

«Проекции по умолчанию» — когда вы передаёте функцию, а не проекцию, в path.projection — больше не поддерживаются. Для географических проекций используйте d3.geoProjection или d3.geoProjectionMutator для определения пользовательской проекции. Для произвольных преобразований геометрии реализуйте интерфейс потока; см. также d3.geoTransform. «Сырые» проекции (например, d3.geo.equirectangular.raw) больше не экспортируются.

Иерархии (d3-hierarchy)

В соответствии с большим сглаживанием пространства имён:

  • d3.layout.cluster ↦ d3.cluster
  • d3.layout.hierarchy ↦ d3.hierarchy
  • … (и так далее)

В качестве альтернативы использованию JSON для представления иерархических данных (например, «flare.json format», используемого во многих примерах D3), новый оператор d3.stratify упрощает преобразование табличных данных в иерархические данные! Это удобно, если у вас уже есть данные в табличном формате, например, результат запроса SQL или файла CSV:

name,parent
Eve,
Cain,Eve
Seth,Eve
Enos,Seth
Noam,Seth
Abel,Eve
Awan,Eve
Enoch,Awan
Azura,Eve

Для преобразования этого в корневой node:

var root = d3.stratify()
    .id(function(d) { return d.name; })
    .parentId(function(d) { return d.parent; })
    (nodes);

Полученный root можно передать в d3.tree, чтобы получить диаграмму дерева, подобную этой:

Корневые узлы также могут быть созданы из данных JSON с помощью d3.hierarchy. Схемы расположения иерархии теперь принимают эти корневые узлы в качестве входных данных вместо непосредственной обработки данных JSON, что способствует более чистому разделению входных данных и вычисляемой структуры. (Например, используйте node.copy, чтобы изолировать изменения в структуре.) Это также упрощает API: вместо того, чтобы каждой схеме расположения иерархии приходилось реализовывать аксессоры для значения и сортировки, теперь существуют общие методы node.sum и node.sort, которые работают с любой схемой расположения иерархии.

Новый API d3.hierarchy также предоставляет более богатый набор методов для обработки данных иерархической структуры. Например, для генерации массива всех узлов в топологическом порядке используйте node.descendants; для узлов-листьев используйте node.leaves. Чтобы выделить предков данного узла при наведении указателя мыши, используйте node.ancestors. Для генерации массива связей {source, target} для данной иерархии используйте node.links; это заменяет методы treemap.links и аналогичные методы для других схем расположения. Новый метод node.path заменяет d3.layout.bundle; см. также d3.curveBundle для иерархического связывания рёбер.

Схемы расположения иерархии были переписаны с использованием новых нерекурсивных методов обхода (node.each, node.eachAfter и node.eachBefore), что улучшает производительность при работе с большими наборами данных. Схема расположения d3.tree больше не использует поле node._ для хранения временного состояния во время расположения.

Разбиение древовидной диаграммы (treemap) теперь расширяемо через treemap.tile! Алгоритм squarified tiling по умолчанию, d3.treemapSquarify, был полностью переписан, что улучшило производительность и исправило ошибки в отступах и округлениях. Метод treemap.sticky был заменён на d3.treemapResquarify, который идентичен d3.treemapSquarify за исключением того, что он выполняет стабильные обновления соседей, сохраняя порядок. Метод treemap.ratio был заменён на squarify.ratio. И есть новый d3.treemapBinary для двоичных treemap!

Отступы древовидной диаграммы (treemap) также были улучшены. Теперь treemap различает внешний отступ, разделяющий родителя и его потомков, и внутренний отступ, разделяющий соседние элементы. Вы можете установить верхний, правый, нижний и левый внешний отступ по отдельности.

Есть новые примеры для традиционной вложенной древовидной диаграммы и для древовидной диаграммы Lü и Fogarty с каскадным расположением. Также есть новый пример, демонстрирующий d3.nest с d3.treemap.

Схемы расположения, заполняющие пространство d3.treemap и d3.partition, теперь выводят x0, x1, y0, y1 для каждого узла вместо x0, dx, y0, dy. Это повышает точность, гарантируя, что края смежных ячеек точно равны, а не немного смещены из-за вычислений с плавающей запятой. Схема расположения partition теперь поддерживает округление и отступы.

Схема расположения круговых пакетов d3.pack была полностью переписана, чтобы лучше реализовать алгоритм Wang et al., исправляя серьёзные ошибки и улучшая результаты! Алгоритм Welzl теперь используется для вычисления точного наименьшего охватывающего круга для каждого родительского элемента, а не приближенного значения, используемого Wang et al. Вывод 3.x показан слева; 4.0 показан справа:

Circle Packing in 3.x Circle Packing in 4.0

Неиерархическая реализация также доступна как d3.packSiblings, а реализация наименьшего охватывающего круга — как d3.packEnclose. Отступ пакетов теперь применяется как между родителем и его детьми, так и между соседними элементами. Кроме того, вы теперь можете указать отступ как функцию, которая вычисляется динамически для каждого родителя.

Внутренние механизмы

Метод d3.rebind был удален. (См. исходный код 3.x.) Если вы хотите обернуть метод getter-setter, рекомендуемый подход — реализовать метод-обёртку и проверить возвращаемое значение. Например, если component использует внутренний dispatch, component.on может повторно привязать dispatch.on следующим образом:

component.on = function() {
  var value = dispatch.on.apply(dispatch, arguments);
  return value === dispatch ? component : value;
};

Метод d3.functor был удален. (См. исходный код 3.x.) Если вы хотите преобразовать константу в функцию, рекомендуемый подход — реализовать замыкание, которое возвращает постоянное значение. При желании вы можете использовать вспомогательный метод следующим образом:

function constant(x) {
  return function() {
    return x;
  };
}

Чтобы преобразовать значение x в функцию, если оно таковым не является:

var fx = typeof x === "function" ? x : constant(x);

Интерполяторы (d3-interpolate)

Метод d3.interpolate больше не делегирует вызов d3.interpolators, который был удалён; его поведение теперь определяется библиотекой. В обычном случае, когда b — число, он работает немного быстрее. Он использует d3.interpolateRgb только в том случае, если b — допустимый CSS-спецификатор цвета (а не приблизительно таковой). И если конечное значение b равно null, undefined, true или false, d3.interpolate теперь возвращает постоянную функцию, которая всегда возвращает b.

Поведение d3.interpolateObject и d3.interpolateArray немного изменилось в отношении свойств или элементов в начальном значении a, которые отсутствуют в конечном значении b: эти свойства и элементы теперь игнорируются, так что конечное значение интерполятора при t = 1 теперь точно равно b. Итак, в 3.x:

d3.interpolateObject({foo: 2, bar: 1}, {foo: 3})(0.5); // {bar: 1, foo: 2.5} in 3.x

В то время как в 4.0 свойство a.bar игнорируется:

d3.interpolateObject({foo: 2, bar: 1}, {foo: 3})(0.5); // {foo: 2.5} in 4.0

Если a или b не определены или не являются объектом, они теперь неявно преобразуются в пустой объект или пустой массив, соответственно, а не в исключение TypeError.

Интерполятор d3.interpolateTransform был переименован в d3.interpolateTransformSvg, а также появился новый d3.interpolateTransformCss для интерполяции CSS-трансформаций! Это позволяет d3-transition автоматически интерполировать как атрибут SVG transform, так и свойство стиля CSS transform. (Обратите внимание, что поддерживаются только 2D CSS-трансформации.) Метод d3.transform был удалён.

Интерполяторы цветовых пространств теперь интерполируют непрозрачность (см. d3-color) и возвращают строки CSS-спецификаторов цвета rgb(…) или rgba(…), а не используя шестнадцатеричный формат RGB. Это необходимо для поддержки интерполяции непрозрачности, но также полезно, потому что это соответствует вычисленным значениям CSS. Когда канал в начальном цвете a не определён, цветовые интерполяторы теперь используют соответствующее значение канала из конечного цвета b, или наоборот. Эта логика ранее применялась к некоторым каналам (например, насыщенности в HSL), но теперь применяется ко всем каналам во всех цветовых пространствах и особенно полезна при интерполяции к прозрачному или из прозрачного.

Теперь существуют «длинные» версии интерполяторов цилиндрических цветовых пространств: d3.interpolateHslLong, d3.interpolateHclLong и d3.interpolateCubehelixLong. Эти интерполяторы используют линейную интерполяцию оттенка, а не кратчайший путь по окружности оттенка 360°. См. d3.interpolateRainbow для примера. Цветовое пространство Cubehelix теперь поддерживается d3-color, поэтому теперь есть интерполяторы d3.interpolateCubehelix и d3.interpolateCubehelixLong.

Интерполяция цвета с учётом гаммы теперь поддерживается для цветовых пространств RGB и Cubehelix в виде interpolate.gamma. Например, чтобы интерполировать от фиолетового к оранжевому с гаммой 2,2 в пространстве RGB:

var interpolate = d3.interpolateRgb.gamma(2.2)("purple", "orange");

Теперь есть новые интерполяторы для однородных нерациональных B-сплайнов! Они полезны для плавной интерполяции между произвольным рядом значений от t = 0 до t = 1, например, для создания плавного градиента цвета из дискретного набора цветов. Интерполяторы d3.interpolateBasis и d3.interpolateBasisClosed генерируют одномерные B-сплайны, а d3.interpolateRgbBasis и d3.interpolateRgbBasisClosed — трёхмерные B-сплайны через цветовое пространство RGB. Они используются d3-scale-chromatic для генерации непрерывных цветовых шкал из дискретных схем цветов ColorBrewer, например PiYG.

Также появился метод d3.quantize для генерации равномерно распределённых дискретных выборок из непрерывного интерполятора. Это полезно для применения одного из встроенных цветовых масштабов (например, d3.interpolateViridis) и квантования его для использования с d3.scaleQuantize, d3.scaleQuantile или d3.scaleThreshold.

Пути (d3-path)

Сериализатор d3.path реализует API CanvasPathMethods, позволяя писать код, который можно отрисовать как в Canvas, так и в SVG. Например, имея код, рисующий на холсте:

function drawCircle(context, radius) {
  context.moveTo(radius, 0);
  context.arc(0, 0, radius, 0, 2 * Math.PI);
}

Вы можете отрисовать его в SVG следующим образом:

var context = d3.path();
drawCircle(context, 40);
pathElement.setAttribute("d", context.toString());

Сериализатор путей позволяет d3-shape поддерживать как Canvas, так и SVG; см., например, line.context и area.context.

Многоугольники (d3-polygon)

Конструктор d3.geom.polygon больше не существует; вместо этого вы просто передаёте массив вершин методам многоугольника. Таким образом, вместо polygon.area и polygon.centroid используются d3.polygonArea и d3.polygonCentroid. Также появились новые методы d3.polygonContains и d3.polygonLength. Эквивалента polygon.clip больше нет, но если необходима обрезка по алгоритму Сутерленда—Ходжмана, пожалуйста, оставьте запрос на новую функцию.

Оператор d3.geom.hull был упрощён: вместо оператора с атрибутами hull.x и hull.y теперь существует просто метод d3.polygonHull, принимающий массив точек и возвращающий выпуклую оболочку.

Четвертичные деревья (d3-quadtree)

Метод d3.geom.quadtree был заменён на d3.quadtree. В версии 4.0 убрано понятие «генераторов» четвертичных деревьев (настраиваемые функции, создающие четвертичное дерево из массива данных); теперь есть только деревья, которые можно создать с помощью d3.quadtree и добавить данные в них с помощью quadtree.add и quadtree.addAll. Этот код из 3.x:

var quadtree = d3.geom.quadtree()
    .extent([[0, 0], [width, height]])
    (data);

Можно переписать в 4.0 как:

var quadtree = d3.quadtree()
    .extent([[0, 0], [width, height]])
    .addAll(data);

Новая реализация четвертичного дерева значительно улучшена! Она больше не рекурсивна, избегая переполнения стека при большом количестве совпадающих точек. Внутреннее хранилище теперь более эффективное, и реализация также быстрее; построение четвертичного дерева из 1 млн нормально распределённых точек занимает примерно одну секунду в 4.0 по сравнению с тремя секундами в 3.x.

Изменение структуры внутренних узлов влияет на quadtree.visit: используйте node.length, чтобы отличить листья от внутренних узлов. Например, чтобы пройти по всем данным в четвертичном дереве:

quadtree.visit(function(node) {
  if (!node.length) {
    do {
      console.log(node.data);
    } while (node = node.next)
  }
});

Есть новый метод quadtree.visitAfter для обхода узлов в постфиксной инфиксной форме обхода. Эта функция используется в d3-force для реализации приближения Барнса—Хатта.

Теперь вы можете удалять данные из четвертичного дерева с помощью quadtree.remove и quadtree.removeAll. При добавлении данных в четвертичное дерево оно теперь расширяет свой диапазон, повторяя удвоение, если новая точка находится за пределами существующего диапазона четвертичного дерева. Также есть методы quadtree.extent и quadtree.cover для явного расширения диапазона четвертичного дерева после создания.

Четвертичные деревья поддерживают несколько новых утилитарных методов: quadtree.copy возвращает копию четвертичного дерева, совместно использующую те же данные; quadtree.data генерирует массив всех данных в четвертичном дереве; quadtree.size возвращает количество точек данных в четвертичном дереве; и quadtree.root возвращает корневой узел, что полезно для ручного обхода четвертичного дерева. Метод quadtree.find теперь принимает необязательный радиус поиска, что полезно для выбора на основе указателя в силах направленных графов.

Очереди (d3-queue)

Ранее известный как Queue.js и queue-async, d3.queue теперь включён в основной пакет, что упрощает параллельную загрузку файлов данных. Он был переписан с меньшим количеством замыканий для повышения производительности, и теперь есть более строгие проверки, чтобы гарантировать чётко определённое поведение. Теперь вы можете использовать instanceof d3.queue и просматривать внутреннее состояние очереди.

Случайные числа (d3-random)

В соответствии с большим сглаживанием пространства имён, генераторы случайных чисел получили новые имена:

  • d3.random.normal ↦ d3.randomNormal
  • d3.random.logNormal ↦ d3.randomLogNormal
  • d3.random.bates ↦ d3.randomBates
  • d3.random.irwinHall ↦ d3.randomIrwinHall

Также появились новые генераторы случайных чисел для экспоненциального и равномерного распределений. Генераторы случайных чисел нормального и логарифмически-нормального распределений были оптимизированы.

Запросы (d3-request)

Метод d3.xhr был переименован в d3.request. Теперь поддерживается базовая аутентификация с помощью request.user и request.password. Теперь можно настроить таймаут с помощью request.timeout.

При возникновении ошибки соответствующее событие ProgressEvent типа «error» теперь передаётся слушателю ошибок, а не XMLHttpRequest. Аналогично, событие ProgressEvent передаётся слушателям событий прогресса, а не с помощью d3.event. Если d3.xml обнаруживает ошибку при разборе XML, эта ошибка теперь сообщается слушателям ошибок, а не возвращается нулевой ответ.

Методы d3.request, d3.text и d3.xml больше не принимают необязательный тип MIME в качестве второго аргумента; используйте request.mimeType вместо этого. Например:

d3.xml("file.svg").mimeType("image/svg+xml").get(function(error, svg) {
  …
});

За исключением d3.html и d3.xml, Node теперь поддерживается через node-XMLHttpRequest.

Масштабы (d3-scale)

В соответствии с большим сглаживанием пространства имён:

  • d3.scale.linear ↦ d3.scaleLinear
  • d3.scale.sqrt ↦ d3.scaleSqrt
  • d3.scale.pow ↦ d3.scalePow
  • d3.scale.log ↦ d3.scaleLog
  • d3.scale.quantize ↦ d3.scaleQuantize
  • d3.scale.threshold ↦ d3.scaleThreshold
  • d3.scale.quantile ↦ d3.scaleQuantile
  • d3.scale.identity ↦ d3.scaleIdentity
  • d3.scale.ordinal ↦ d3.scaleOrdinal
  • d3.time.scale ↦ d3.scaleTime
  • d3.time.scale.utc ↦ d3.scaleUtc

Теперь масштабы генерируют метки в том же порядке, что и область: если у вас убывающая область, вы теперь получаете убывающие метки. Это изменение влияет на порядок элементов меток, генерируемых осями. Например:

d3.scaleLinear().domain([10, 0]).ticks(5); // [10, 8, 6, 4, 2, 0]

Форматирование меток логарифма теперь предполагает значение по умолчанию count равное десяти, а не бесконечность, если не указано иное. Логарифмические масштабы с областями, охватывающими много степеней (например, от 1e+3 до 1e+29), теперь возвращают только одну метку на степень, а не основание меток на степень. Нелинейные количественные масштабы несколько точнее.

Теперь вы можете контролировать, будет ли область порядкового масштаба неявно расширяться, когда масштабу передаётся значение, которое ещё не существует в его области. По умолчанию, ordinal.unknown равно d3.scaleImplicit, что приводит к добавлению неизвестных значений в область:

var x = d3.scaleOrdinal()
    .domain([0, 1])
    .range(["red", "green", "blue"]);

x.domain(); // [0, 1]
x(2); // "blue"
x.domain(); // [0, 1, 2]

Установив ordinal.unknown, вы вместо этого определяете выходное значение для неизвестных входных данных. Это особенно полезно для картограмм, где вы хотите назначить цвет отсутствующим данным.

var x = d3.scaleOrdinal()
    .domain([0, 1])
    .range(["red", "green", "blue"])
    .unknown(undefined);

x.domain(); // [0, 1]
x(2); // undefined
x.domain(); // [0, 1]

Методы ordinal.rangeBands и ordinal.rangeRoundBands были заменены новым подклассом порядкового масштаба: полосы масштабов. Следующий код из 3.x:

var x = d3.scale.ordinal()
    .domain(["a", "b", "c"])
    .rangeBands([0, width]);

Эквивалентен этому в 4.0:

var x = d3.scaleBand()
    .domain(["a", "b", "c"])
    .range([0, width]);

Новые методы band.padding, band.paddingInner и band.paddingOuter заменяют необязательные аргументы метода ordinal.rangeBands. Новые методы band.bandwidth и band.step заменяют метод ordinal.rangeBand. Также есть новый метод band.align, который позволяет управлять распределением дополнительного пространства вне полос, например, для сдвига столбцов ближе к оси y.

Аналогично, методы ordinal.rangePoints и ordinal.rangeRoundPoints были заменены новым подклассом шкалы ordinal: шкалы точек. Следующий код из версии 3.x:

var x = d3.scale.ordinal()
    .domain(["a", "b", "c"])
    .rangePoints([0, width]);

Эквивалентен этому коду в версии 4.0:

var x = d3.scalePoint()
    .domain(["a", "b", "c"])
    .range([0, width]);

Новый метод point.padding заменяет необязательный аргумент padding метода ordinal.rangePoints. Как и метод ordinal.rangeBand с ordinal.rangePoints, метод point.bandwidth всегда возвращает ноль; новый метод point.step возвращает интервал между смежными точками.

Конструктор шкалы ordinal теперь принимает необязательный аргумент range для сокращённой записи вместо ordinal.range. Это особенно полезно теперь, когда шкалы категорических цветов изменены на простые массивы цветов вместо специализированных конструкторов шкал ordinal:

  • d3.scale.category10 ↦ d3.schemeCategory10
  • d3.scale.category20 ↦ d3.schemeCategory20
  • d3.scale.category20b ↦ d3.schemeCategory20b
  • d3.scale.category20c ↦ d3.schemeCategory20c

Следующий код из версии 3.x:

var color = d3.scale.category10();

Эквивалентен этому коду в версии 4.0:

var color = d3.scaleOrdinal(d3.schemeCategory10);

Последовательные шкалы — новый класс шкал с фиксированным выходным интерполятором вместо диапазона. Обычно эти шкалы используются для реализации непрерывных последовательных или разделяющих цветовых схем. Вдохновлённые новыми цветовыми картами с учётом восприятия цвета из Matplotlib, в версии 4.0 добавлены интерполяторы viridis, inferno, magma, plasma для использования с последовательными шкалами. Используя d3.quantize, эти интерполяторы также могут быть применены к квантильным, квантующим и пороговым шкалам.

viridis inferno magma plasma

В версии 4.0 также добавлены новые схемы Cubehelix, включая по умолчанию от Dave Green и циклическую радугу, вдохновлённую Matteo Niccoli:

cubehelix rainbow warm cool

Для ещё большего набора последовательных и категорических цветовых схем, см. d3-scale-chromatic.

Для введения в шкалы, см. Введение в d3-scale.

Выборки (d3-selection)

Выборки больше не наследуются от массива с использованием вставки в цепочку прототипов; теперь они являются обычными объектами, что улучшает производительность. Внутренние поля (selection._groups, selection._parents) являются приватными; пожалуйста, используйте документированный публичный API для управления выборками. Новый метод selection.nodes генерирует массив всех узлов в выборке.

Выборки теперь неизменяемы: элементы и родители в выборке никогда не меняются. (Атрибуты и содержимое элементов, конечно, всё ещё могут быть изменены!) Методы selection.sort и selection.data теперь возвращают новые выборки, а не изменяют существующую выборку на месте. Кроме того, selection.append больше не объединяет входящие узлы с обновляемой выборкой; используйте selection.merge для объединения входящих и обновляемых выборок после объединения данных. Например, следующий общий шаблон обновления из версии 3.x:

var circle = svg.selectAll("circle").data(data) // UPDATE
    .style("fill", "blue");

circle.exit().remove(); // EXIT

circle.enter().append("circle") // ENTER; modifies UPDATE! 🌶
    .style("fill", "green");

circle // ENTER + UPDATE
    .style("stroke", "black");

Будет переписан в версии 4.0 как:

var circle = svg.selectAll("circle").data(data) // UPDATE
    .style("fill", "blue");

circle.exit().remove(); // EXIT

circle.enter().append("circle") // ENTER
    .style("fill", "green")
  .merge(circle) // ENTER + UPDATE
    .style("stroke", "black");

Это изменение обсуждается подробнее в Что делает программное обеспечение хорошим.

В версии 3.x методы selection.enter и selection.exit были не определены до вызова selection.data, что приводило к ошибке TypeError, если вы пытались к ним обратиться. В версии 4.0 они просто возвращают пустую выборку, если выборка не была связана с данными.

В версии 3.x, selection.append всегда добавлял новый элемент как последнего потомка его родителя. Небольшая хитрость заключалась в использовании selection.insert без указания селектора before при вставке узлов, что приводило к вставке входящих узлов перед следующим элементом в обновляемой выборке. В версии 4.0 это поведение по умолчанию метода selection.append; если вы не указываете селектор before для selection.insert, вставленный элемент добавляется как последний потомок. Это изменение сохраняет относительный порядок элементов и данных в общем шаблоне обновления. Например, с данным DOM:

<div>a</div>
<div>b</div>
<div>f</div>

И следующим кодом:

var div = d3.select("body").selectAll("div")
  .data(["a", "b", "c", "d", "e", "f"], function(d) { return d || this.textContent; });

div.enter().append("div")
    .text(function(d) { return d; });

Результат будет таким:

<div>a</div>
<div>b</div>
<div>c</div>
<div>d</div>
<div>e</div>
<div>f</div>

Таким образом, входящие c, d и e вставляются перед f, так как f — следующий элемент в обновляемой выборке. Хотя этого достаточно для сохранения порядка, если порядок новых данных стабилен, если порядок данных меняется, вам всё ещё нужно использовать selection.order для переупорядочивания элементов.

Теперь существует только один класс выборок. Версия 3.x реализовывала входящие выборки с использованием специального класса с различным поведением для enter.append и enter.select; следствием этого дизайна было то, что входящие выборки в версии 3.x не имели некоторых методов. В версии 4.0 входящие выборки являются обычными выборками; они имеют те же методы и то же поведение. Плейсхолдеры входящих узлов теперь реализуют node.appendChild, node.insertBefore, node.querySelector и node.querySelectorAll.

Метод selection.data немного изменён в отношении дублирующихся ключей. В версии 3.x, если несколько данных имели одинаковый ключ, дублирующиеся данные игнорировались и не включались в входящие, обновляемые или выходящие данные; в версии 4.0 дублирующиеся данные всегда помещаются в входящую выборку. В обеих версиях (3.x и 4.0), если несколько элементов имеют одинаковый ключ, дублирующиеся элементы помещаются в выборку выходящих элементов. Таким образом, поведение версии 4.0 теперь симметрично для входящих и выходящих выборок, и общий шаблон обновления теперь создаст DOM, соответствующий данным, даже если существуют дублирующиеся ключи.

Выборки имеют несколько новых методов! Используйте selection.raise для перемещения выбранных элементов вперёд среди своих братьев, чтобы они отображались сверху; используйте selection.lower для перемещения их назад. Используйте selection.dispatch для отправки пользовательского события слушателям событий.

При вызове в режиме получения данных, selection.data теперь возвращает данные для всех элементов в выборке, а не только для первой группы элементов. Метод selection.call больше не устанавливает контекст this при вызове указанной функции; выборка передаётся как первый аргумент функции, поэтому используйте её. Метод selection.on теперь принимает несколько типов событий, разделённых пробелами, так что вы можете одновременно добавить или удалить нескольких слушателей. Например:

selection.on("mousedown touchstart", function() {
  console.log(d3.event.type);
});

Аргументы, передаваемые функциям обратного вызова, немного изменились в версии 4.0 для большей согласованности. Стандартные аргументы — данные элемента (d), индекс элемента (i) и группа элемента (nodes), с this как элемент. Единственное исключение из этой конвенции — selection.data, который оценивается для каждой группы, а не для каждого элемента; он получает данные родителя группы (d), индекс группы (i) и родителей выборки (parents), с this как родителя группы.

Новый d3.local предоставляет механизм для определения локальных переменных: состояние, привязанное к элементам DOM и доступное любому дочернему элементу. Это может быть удобной альтернативой использованию selection.each или сохранению локального состояния в данных.

Пространство имен d3.ns.prefix заменено на d3.namespaces, а метод d3.ns.qualify — на d3.namespace. Доступны также несколько новых методов низкого уровня. d3.matcher используется внутри selection.filter; d3.selector — в selection.select; d3.selectorAll — в selection.selectAll; d3.creator — в selection.append и selection.insert. Новый метод d3.window возвращает окно-владелец для данного элемента, окна или документа. Новый метод d3.customEvent временно устанавливает d3.event при вызове функции, что позволяет реализовать управление, которое отправляет пользовательские события; этот метод используется в d3-drag, d3-zoom и d3-brush.

Для экономии места методы с множественными значениями (где вы передаёте объект для одновременной установки нескольких атрибутов, стилей или свойств) вынесены в d3-selection-multi и больше не входят в базовый набор. Методы отображения множественных значений также переименованы в множественное число, чтобы снизить перегрузку: selection.attrs, selection.styles и selection.properties.

Фигуры (d3-shape)

В соответствии с большим сглаживанием пространства имён:

  • d3.svg.line ↦ d3.line
  • d3.svg.line.radial ↦ d3.radialLine
  • d3.svg.area ↦ d3.area
  • d3.svg.area.radial ↦ d3.radialArea
  • d3.svg.arc ↦ d3.arc
  • d3.svg.symbol ↦ d3.symbol
  • d3.svg.symbolTypes ↦ d3.symbolTypes
  • d3.layout.pie ↦ d3.pie
  • d3.layout.stack ↦ d3.stack
  • d3.svg.diagonal ↦ УДАЛЕНО (см. d3/d3-shape#27)
  • d3.svg.diagonal.radial ↦ УДАЛЕНО

Фигуры больше не ограничены SVG; они теперь могут отображаться на холсте! Генераторы фигур теперь поддерживают необязательный параметр context: при заданном CanvasRenderingContext2D вы можете отобразить фигуру как путь холста для заполнения или обводки. Например, круговая диаграмма на холсте может использовать генератор дуг:

var arc = d3.arc()
    .outerRadius(radius - 10)
    .innerRadius(0)
    .context(context);

Чтобы отобразить дугу для данного элемента данных d:

context.beginPath();
arc(d);
context.fill();

См. line.context, area.context и arc.context для получения дополнительной информации. Внутри фигур используется d3-path для сериализации методов путей холста в данные пути SVG, когда контекст равен null; таким образом, фигуры оптимизированы для отображения на холсте. Теперь также можно создавать линии из областей. Линия разделяет большинство тех же аксессоров, таких как line.defined и line.curve, с областью, из которой она получена. Например, чтобы отобразить верхнюю линию области, используйте area.lineY1; для нижней линии — area.lineY0.

Версия 4.0 вводит новый API кривых для указания способа интерполяции между точками данных для линий и областей. Методы line.interpolate и area.interpolate заменены на line.curve и area.curve. Кривые реализованы с помощью интерфейса кривых, а не как функция, возвращающая строку данных пути SVG; это позволяет кривым отображаться как на SVG, так и на холсте. Кроме того, line.curve и area.curve теперь принимают функцию, которая инициализирует кривую для заданного context, а не строку. Полный список эквивалентов:

  • linear ↦ d3.curveLinear
  • linear-closed ↦ d3.curveLinearClosed
  • step ↦ d3.curveStep
  • step-before ↦ d3.curveStepBefore
  • step-after ↦ d3.curveStepAfter
  • basis ↦ d3.curveBasis
  • basis-open ↦ d3.curveBasisOpen
  • basis-closed ↦ d3.curveBasisClosed
  • bundle ↦ d3.curveBundle
  • cardinal ↦ d3.curveCardinal
  • cardinal-open ↦ d3.curveCardinalOpen
  • cardinal-closed ↦ d3.curveCardinalClosed
  • monotone ↦ d3.curveMonotoneX

Но это ещё не всё! Версия 4.0 теперь предоставляет параметризованные сплайны Catmull–Rom, как предложено Yuksel и др.. Они доступны как d3.curveCatmullRom, d3.curveCatmullRomClosed и d3.curveCatmullRomOpen.

catmullRom

catmullRomOpen

catmullRomClosed

Каждый тип кривой может определять свои собственные именованные параметры, заменяя line.tension и area.tension. Например, сплайны Catmull–Rom параметризуются с помощью catmullRom.alpha и по умолчанию равны 0,5, что соответствует центральному сплайну, который избегает самопересечения и перескока. Для унифицированного сплайна Catmull–Rom вместо этого:

var line = d3.line()
    .curve(d3.curveCatmullRom.alpha(0));

Версия 4.0 исправляет интерпретацию параметра натяжения кардинального сплайна, который теперь задаётся как cardinal.tension и по умолчанию равен нулю для унифицированного сплайна Catmull–Rom; натяжение 1 даёт линейную кривую. Первые и последние сегменты кривых basis и cardinal также исправлены! Недокументированное поле interpolate.reverse удалено. Кривые могут определять различное поведение для верхних и нижних линий, подсчитывая последовательность curve.lineStart в curve.areaStart. См. реализацию d3.curveStep для примера.

Версия 4.0 исправляет многочисленные ошибки в реализации кривой monotone и вводит d3.curveMonotoneY; это аналогично d3.curveMonotoneX, за исключением того, что требует, чтобы входные точки были монотонными по y, а не по x, например, для вертикальной диаграммы линий. Новая кривая d3.curveNatural создаёт естественный кубический сплайн. Значение по умолчанию β для d3.curveBundle теперь равно 0,85, а не 0,7, что соответствует значениям, используемым Holten. Версия 4.0 также имеет более надёжную реализацию отступа дуги; см. arc.padAngle и arc.padRadius.

Версия 4.0 вводит новый API типов символов. Типы символов передаются в symbol.type вместо строк. Эквиваленты:

  • circle ↦ d3.symbolCircle
  • cross ↦ d3.symbolCross
  • diamond ↦ d3.symbolDiamond
  • square ↦ d3.symbolSquare
  • triangle-down ↦ УДАЛЕНО
  • triangle-up ↦ d3.symbolTriangle
  • ДОБАВЛЕНО ↦ d3.symbolStar
  • ДОБАВЛЕНО ↦ d3.symbolWye

Полный набор типов символов теперь:

Наконец, версия 4.0 перерабатывает API компоновки стека, заменяя d3.layout.stack на d3.stack. Генератору стека больше не нужен аксессор x. Кроме того, API упрощён: генератор stack теперь принимает табличные данные, такие как этот массив объектов:

var data = [
  {month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
  {month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
  {month: new Date(2015, 2, 1), apples:  640, bananas:  960, cherries: 640, dates: 400},
  {month: new Date(2015, 3, 1), apples:  320, bananas:  480, cherries: 640, dates: 400}
];

Чтобы сгенерировать компоновку стека, сначала определите генератор стека, а затем примените его к данным:

var stack = d3.stack()
    .keys(["apples", "bananas", "cherries", "dates"])
    .order(d3.stackOrderNone)
    .offset(d3.stackOffsetNone);

var series = stack(data);

Получившийся массив имеет один элемент на ряд. Каждый ряд имеет одну точку на месяц, и каждая точка имеет нижнее и верхнее значения, определяющие нижнюю и верхнюю границы:

[
  [[   0, 3840], [   0, 1600], [   0,  640], [   0,  320]], // apples
  [[3840, 5760], [1600, 3040], [ 640, 1600], [ 320,  800]], // bananas
  [[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
  [[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // dates
]

Каждый ряд затем обычно передаётся в генератор областей для отображения диаграммы областей или используется для построения прямоугольников для столбчатой диаграммы. Генераторы стека больше не изменяют входные данные, поэтому stack.out удалён.

Для ознакомления с фигурами см. Представление d3-shape.

Форматы времени (d3-time-format)

В соответствии с большим сглаживанием пространства имён, конструкторы форматов получили новые имена:

  • d3.time.format ↦ d3.timeFormat
  • d3.time.format.utc ↦ d3.utcFormat
  • d3.time.format.iso ↦ d3.isoFormat

Метод формата.parse также был удален в пользу отдельных конструкторов парсеров d3.timeParse, d3.utcParse и d3.isoParse. Таким образом, этот код в версии 3.x:

var parseTime = d3.time.format("%c").parse;

Может быть переписан в версии 4.0 как:

var parseTime = d3.timeParse("%c");

Формат времени с несколькими шкалами d3.time.format.multi был заменен на формат шкалы d3.scaleTime’s формат разметки. Форматы времени теперь преобразуют входные данные в даты, а парсеры времени — в строки. Директива %Z теперь позволяет более гибкую обработку смещений часовых поясов, таких как -0700, -07:00, -07, и Z. Директива %p теперь обрабатывается правильно, когда имя периода в локали длиннее двух символов (например, «a.m.»).

По умолчанию используется локали США на английском языке с 12-часовым форматом времени и более кратким представлением даты. Это соответствует местным правилам и согласуется с date.toLocaleString в Chrome, Firefox и Node:

var now = new Date;
d3.timeFormat("%c")(new Date); // "6/23/2016, 2:01:33 PM"
d3.timeFormat("%x")(new Date); // "6/23/2016"
d3.timeFormat("%X")(new Date); // "2:01:38 PM"

Теперь можно установить локаль по умолчанию с помощью d3.timeFormatDefaultLocale! Локали публикуются как JSON в npm.

Производительность форматирования и анализа времени была улучшена, а форматировщик и анализатор UTC имеют более чистое исполнение (что избегает временного переопределения глобальной переменной Date).

Интервалы времени (d3-time)

В соответствии с большим сглаживанием пространства имен, интервалы местного времени были переименованы:

  • ADDED ↦ d3.timeMillisecond
  • d3.time.second ↦ d3.timeSecond
  • d3.time.minute ↦ d3.timeMinute
  • d3.time.hour ↦ d3.timeHour
  • d3.time.day ↦ d3.timeDay
  • d3.time.sunday ↦ d3.timeSunday
  • d3.time.monday ↦ d3.timeMonday
  • d3.time.tuesday ↦ d3.timeTuesday
  • d3.time.wednesday ↦ d3.timeWednesday
  • d3.time.thursday ↦ d3.timeThursday
  • d3.time.friday ↦ d3.timeFriday
  • d3.time.saturday ↦ d3.timeSaturday
  • d3.time.week ↦ d3.timeWeek
  • d3.time.month ↦ d3.timeMonth
  • d3.time.year ↦ d3.timeYear

Интервалы времени UTC также были переименованы:

  • ADDED ↦ d3.utcMillisecond
  • d3.time.second.utc ↦ d3.utcSecond
  • d3.time.minute.utc ↦ d3.utcMinute
  • d3.time.hour.utc ↦ d3.utcHour
  • d3.time.day.utc ↦ d3.utcDay
  • d3.time.sunday.utc ↦ d3.utcSunday
  • d3.time.monday.utc ↦ d3.utcMonday
  • d3.time.tuesday.utc ↦ d3.utcTuesday
  • d3.time.wednesday.utc ↦ d3.utcWednesday
  • d3.time.thursday.utc ↦ d3.utcThursday
  • d3.time.friday.utc ↦ d3.utcFriday
  • d3.time.saturday.utc ↦ d3.utcSaturday
  • d3.time.week.utc ↦ d3.utcWeek
  • d3.time.month.utc ↦ d3.utcMonth
  • d3.time.year.utc ↦ d3.utcYear

Псевдонимы диапазонов местного времени были переименованы:

  • d3.time.seconds ↦ d3.timeSeconds
  • d3.time.minutes ↦ d3.timeMinutes
  • d3.time.hours ↦ d3.timeHours
  • d3.time.days ↦ d3.timeDays
  • d3.time.sundays ↦ d3.timeSundays
  • d3.time.mondays ↦ d3.timeMondays
  • d3.time.tuesdays ↦ d3.timeTuesdays
  • d3.time.wednesdays ↦ d3.timeWednesdays
  • d3.time.thursdays ↦ d3.timeThursdays
  • d3.time.fridays ↦ d3.timeFridays
  • d3.time.saturdays ↦ d3.timeSaturdays
  • d3.time.weeks ↦ d3.timeWeeks
  • d3.time.months ↦ d3.timeMonths
  • d3.time.years ↦ d3.timeYears

Псевдонимы диапазонов времени UTC были переименованы:

  • d3.time.seconds.utc ↦ d3.utcSeconds
  • d3.time.minutes.utc ↦ d3.utcMinutes
  • d3.time.hours.utc ↦ d3.utcHours
  • d3.time.days.utc ↦ d3.utcDays
  • d3.time.sundays.utc ↦ d3.utcSundays
  • d3.time.mondays.utc ↦ d3.utcMondays
  • d3.time.tuesdays.utc ↦ d3.utcTuesdays
  • d3.time.wednesdays.utc ↦ d3.utcWednesdays
  • d3.time.thursdays.utc ↦ d3.utcThursdays
  • d3.time.fridays.utc ↦ d3.utcFridays
  • d3.time.saturdays.utc ↦ d3.utcSaturdays
  • d3.time.weeks.utc ↦ d3.utcWeeks
  • d3.time.months.utc ↦ d3.utcMonths
  • d3.time.years.utc ↦ d3.utcYears

Поведение interval.range (и псевдонимы для удобства, такие как d3.timeDays) изменилось, когда шаг больше единицы. Вместо фильтрации возвращаемых дат по номеру поля, interval.range теперь ведет себя как d3.range: он просто пропускает, возвращая каждую шаг-ю дату. Например, следующий код в версии 3.x возвращает только нечетные дни месяца:

d3.time.days(new Date(2016, 4, 28), new Date(2016, 5, 5), 2);
// [Sun May 29 2016 00:00:00 GMT-0700 (PDT),
//  Tue May 31 2016 00:00:00 GMT-0700 (PDT),
//  Wed Jun 01 2016 00:00:00 GMT-0700 (PDT),
//  Fri Jun 03 2016 00:00:00 GMT-0700 (PDT)]

Обратите внимание, что возвращаемый массив дат не начинается с даты начала, потому что 28 мая — четное число. Также обратите внимание, что 31 мая и 1 июня находятся на расстоянии одного дня, а не двух! Поведение d3.timeDays в версии 4.0, вероятно, ближе к тому, чего вы ожидаете:

d3.timeDays(new Date(2016, 4, 28), new Date(2016, 5, 5), 2);
// [Sat May 28 2016 00:00:00 GMT-0700 (PDT),
//  Mon May 30 2016 00:00:00 GMT-0700 (PDT),
//  Wed Jun 01 2016 00:00:00 GMT-0700 (PDT),
//  Fri Jun 03 2016 00:00:00 GMT-0700 (PDT)]

Если вам нужна отфильтрованная версия интервала времени (скажем, для гарантии согласованности двух перекрывающихся диапазонов, например, при генерации разметки шкалы времени), вы можете использовать новый метод interval.every или его более общий аналог interval.filter:

d3.timeDay.every(2).range(new Date(2016, 4, 28), new Date(2016, 5, 5));
// [Sun May 29 2016 00:00:00 GMT-0700 (PDT),
//  Tue May 31 2016 00:00:00 GMT-0700 (PDT),
//  Wed Jun 01 2016 00:00:00 GMT-0700 (PDT),
//  Fri Jun 03 2016 00:00:00 GMT-0700 (PDT)]

Интервалы времени теперь предоставляют метод interval.count для подсчета количества границ интервала после даты начала и до или равной даты окончания. Это заменяет методы d3.time.dayOfYear и аналогичные методы в 3.x. Например, этот код в 3.x:

var now = new Date;
d3.time.dayOfYear(now); // 165

Может быть переписан в версии 4.0 как:

var now = new Date;
d3.timeDay.count(d3.timeYear(now), now); // 165

Аналогично, вместо d3.time.weekOfYear в 3.x в 4.0 вы напишите:

d3.timeWeek.count(d3.timeYear(now), now); // 24

Новый interval.count, конечно, более общий. Например, вы можете использовать его для вычисления часа недели для тепловой карты:

d3.timeHour.count(d3.timeWeek(now), now); // 64

Вот все эквивалентности от версии 3.x к 4.0:

  • d3.time.dayOfYear ↦ d3.timeDay.count
  • d3.time.sundayOfYear ↦ d3.timeSunday.count
  • d3.time.mondayOfYear ↦ d3.timeMonday.count
  • d3.time.tuesdayOfYear ↦ d3.timeTuesday.count
  • d3.time.wednesdayOfYear ↦ d3.timeWednesday.count
  • d3.time.thursdayOfYear ↦ d3.timeThursday.count
  • d3.time.fridayOfYear ↦ d3.timeFriday.count
  • d3.time.saturdayOfYear ↦ d3.timeSaturday.count
  • d3.time.weekOfYear ↦ d3.timeWeek.count
  • d3.time.dayOfYear.utc ↦ d3.utcDay.count
  • d3.time.sundayOfYear.utc ↦ d3.utcSunday.count
  • d3.time.mondayOfYear.utc ↦ d3.utcMonday.count
  • d3.time.tuesdayOfYear.utc ↦ d3.utcTuesday.count
  • d3.time.wednesdayOfYear.utc ↦ d3.utcWednesday.count
  • d3.time.thursdayOfYear.utc ↦ d3.utcThursday.count
  • d3.time.fridayOfYear.utc ↦ d3.utcFriday.count
  • d3.time.saturdayOfYear.utc ↦ d3.utcSaturday.count
  • d3.time.weekOfYear.utc ↦ d3.utcWeek.count
END_OF_DOCUMENT_MARKER

D3 4.0 теперь также позволяет определять пользовательские временные интервалы с помощью d3.timeInterval. Интервалы d3.timeYear, d3.utcYear, d3.timeMillisecond и d3.utcMillisecond имеют оптимизированные реализации interval.every, что необходимо для эффективного генерации временных отметки для очень больших или очень малых областей. В целом, производительность временных интервалов была улучшена, и временные интервалы теперь лучше работают с учетом перехода на летнее/зимнее время в различных локалях.

Таймеры (d3-timer)

В D3 3.x единственный способ остановить таймер — это если его обратный вызов вернул true. Например, этот таймер останавливается после одной секунды:

d3.timer(function(elapsed) {
  console.log(elapsed);
  return elapsed >= 1000;
});

В 4.0 используйте timer.stop вместо этого:

var t = d3.timer(function(elapsed) {
  console.log(elapsed);
  if (elapsed >= 1000) {
    t.stop();
  }
});

Основное преимущество timer.stop заключается в том, что таймеры не обязаны самоликвидироваться: их можно остановить извне, что позволяет немедленно и синхронно утилизировать связанные ресурсы и разделение забот. Вышеприведенный пример эквивалентен:

var t = d3.timer(function(elapsed) {
  console.log(elapsed);
});

d3.timeout(function() {
  t.stop();
}, 1000);

Это улучшение распространяется на d3-transition: теперь при прерывании перехода его ресурсы освобождаются немедленно, а не ожидая начала перехода.

4.0 также вводит новый метод timer.restart для перезапуска таймеров, для замены обратного вызова работающего таймера или для изменения его задержки или времени отсчёта. В отличие от timer.stop, за которым следует d3.timer, timer.restart сохраняет приоритет вызова существующего таймера: он гарантирует, что порядок вызова активных таймеров остаётся неизменным. Метод d3.timer.flush был переименован в d3.timerFlush.

Некоторые шаблоны использования в D3 3.x могли привести к зависанию браузера, когда фоновая страница возвращалась на передний план. Например, следующий код планирует переход каждую секунду:

setInterval(function() {
  d3.selectAll("div").transition().call(someAnimation); // BAD
}, 1000);

Если такой код работает в фоновом режиме в течение нескольких часов, тысячи очереди переходов будут пытаться запуститься одновременно, когда страница перейдёт на передний план. D3 4.0 избегает зависания, замораживая время в фоновом режиме: когда страница находится в фоновом режиме, время не продвигается, и поэтому очередь таймеров не накапливается для выполнения при возвращении страницы на передний план. Используйте d3.timer вместо переходов для планирования долговременной анимации или используйте d3.timeout и d3.interval вместо setTimeout и setInterval, чтобы предотвратить формирование очереди переходов в фоновом режиме:

d3.interval(function() {
  d3.selectAll("div").transition().call(someAnimation); // GOOD
}, 1000);

Замораживая время в фоновом режиме, таймеры фактически «не замечают» того, что они находятся в фоновом режиме. Будто ничего не произошло! 4.0 также теперь использует высокоточное время (performance.now), где доступно; текущее время доступно как d3.now.

Переходы (d3-transition)

Метод selection.transition теперь принимает необязательный экземпляр transition, который может использоваться для синхронизации нового перехода с существующим переходом. (Это изменение обсуждается подробнее в Что делает программное обеспечение хорошим?) Например:

var t = d3.transition()
    .duration(750)
    .ease(d3.easeLinear);

d3.selectAll(".apple").transition(t)
    .style("fill", "red");

d3.selectAll(".orange").transition(t)
    .style("fill", "orange");

Переходы, созданные таким образом, наследуют тайминг от ближайшего родительского элемента и, следовательно, синхронизированы, даже если у ссылки transition переменный тайминг, например, с отложенным интервалом. Этот метод заменяет глубоко магическое поведение transition.each в 3.x; в 4.0, transition.each идентичен selection.each. Используйте новый метод transition.on для прослушивания событий перехода.

Значение transition.delay изменилось для цепочечных переходов, созданных с помощью transition.transition. Указанная задержка теперь относится к предыдущему переходу в цепочке, а не к первому переходу в цепочке; это упрощает вставку промежуточных пауз. Например:

d3.selectAll(".apple")
  .transition() // First fade to green.
    .style("fill", "green")
  .transition() // Then red.
    .style("fill", "red")
  .transition() // Wait one second. Then brown, and remove.
    .delay(1000)
    .style("fill", "brown")
    .remove();

Время теперь заморожено в фоновом режиме; см. d3-timer для получения дополнительной информации. Раньше переходы не выполнялись в фоновом режиме, но теперь они возобновляются там, где остановились, когда страница возвращается на передний план. Это предотвращает зависание страницы, не планируя неограниченное число переходов в фоновом режиме. Если вы хотите запланировать бесконечно повторяющийся переход, используйте события переходов или используйте d3.timeout и d3.interval вместо setTimeout и setInterval.

Метод selection.interrupt теперь отменяет все запланированные переходы на выбранных элементах, помимо прерывания любого активного перехода. При прерывании переходов все ресурсы, связанные с переходом, теперь освобождаются немедленно, а не ожидают начала перехода, что улучшает производительность. (См. также timer.stop.) Новый метод d3.interrupt — это альтернатива selection.interrupt для быстрого прерывания одного узла.

Новый метод d3.active позволяет выбрать текущий активный переход на заданном узле, если таковой имеется. Это полезно для изменения текущих переходов и для планирования бесконечно повторяющихся переходов. Например, этот переход непрерывно колеблется между красным и синим:

d3.select("circle")
  .transition()
    .on("start", function repeat() {
        d3.active(this)
            .style("fill", "red")
          .transition()
            .style("fill", "blue")
          .transition()
            .on("start", repeat);
      });

Жизненный цикл перехода жизненный цикл перехода теперь более формально определён и принудительно выполняется. Например, попытка изменить длительность работающего перехода теперь вызывает ошибку, а не просто проваливается. Метод transition.remove был исправлен, если используется несколько имён переходов: элемент удаляется только если у него нет запланированных переходов, независимо от имени. Метод transition.ease теперь всегда принимает функцию сглаживания функцию сглаживания, а не строку. Когда переход заканчивается, интерполяторы вызываются один последний раз со значением t, равным ровно 1, независимо от связанной функции сглаживания.

Как и с выборками в 4.0, все функции обратного вызова переходов теперь получают стандартные аргументы: данные элемента (d), индекс элемента (i) и группа элемента (nodes), с this как элемент. Это существенно влияет на transition.attrTween и transition.styleTween, которые больше не передают функцию интерполяции текущее значение атрибута или стиля в качестве третьего аргумента. Методы transition.attrTween и transition.styleTween теперь могут быть вызваны в режиме получения для отладки или для совместного определения интерполяции между переходами.

Однородные переходы теперь оптимизированы! Если все элементы в переходе используют ту же интерполяцию, интерполятор или обработчики событий, это состояние теперь совместно используется для перехода, а не выделяется по отдельности для каждого элемента. 4.0 также использует оптимизированный интерполятор по умолчанию вместо d3.interpolate для transition.attr и transition.style. И переходы теперь могут интерполировать как CSS, так и SVG преобразования.

Для повторно используемых компонентов, которые поддерживают переходы, таких как оси, новый метод transition.selection возвращает выборку, соответствующую данному переходу. Также есть новый метод transition.merge, который эквивалентен selection.merge.

В целях лаконичности методы карты многозначных значений были извлечены в d3-selection-multi и больше не входят в основной набор. Методы карты многозначных значений также были переименованы в множественное число, чтобы уменьшить перегрузку: transition.attrs и transition.styles.

Диаграммы Вороного (d3-voronoi)

Метод d3.geom.voronoi был переименован в d3.voronoi, а метод voronoi.clipExtent — в voronoi.extent. Недокументированное свойство polygon.point в 3.x, которое представляет собой элемент в входных данных, соответствующий многоугольнику, было переименовано в polygon.data.

Вызов voronoi теперь возвращает полную диаграмму Вороного, которая включает топологическую информацию: каждый край Вороного раскрывает edge.left и edge.right, указывающие на узлы с обеих сторон края, и каждый ячейка Вороного определяется как массив этих рёбер и соответствующего узла. Диаграмма Вороного может быть использована для эффективного вычисления как диаграммы Вороного, так и триангуляции Делоне для набора точек: diagram.polygons, diagram.links и diagram.triangles. Новая топология также полезна в сочетании с TopoJSON; см. пример топологии Вороного пример топологии Вороного.

Функции voronoi.polygons и diagram.polygons теперь требуют параметр extent; неявного значения ±1e6 больше нет. Функции voronoi.links, voronoi.triangles, diagram.links и diagram.triangles теперь зависят от параметра клип-масштабирования: поскольку триангуляция Делоне вычисляется как дуальная к Вороной, две точки связываются только если соответствующие ячейки соприкасаются после применения клип-масштабирования. Для вычисления триангуляции Делоне без учёта клип-масштабирования задайте extent равным null.

Генератор Вороной теперь корректно обрабатывает совпадающие вершины: первая из набора совпадающих точек имеет определённую ячейку, в то время как последующие дубликаты имеют ячейки с значением null. Возвращаемый массив полигонов является разреженным, поэтому с помощью array.forEach или array.map можно легко пропустить неопределённые ячейки. Генератор Вороной теперь также корректно обрабатывает случаи, когда ни одна грань ячейки не пересекает область extent.

Масштабирование (d3-zoom)

Функция масштабирования d3.behavior.zoom была переименована в d3.zoom. Функции масштабирования больше не хранят активное преобразование масштабирования (т.е., видимую область; масштаб и сдвиг) внутренне. Преобразование масштабирования теперь хранится в элементах, к которым применена функция масштабирования. Преобразование масштабирования доступно как event.transform в событии масштабирования или путём вызова d3.zoomTransform для заданного элемента. Для программированного масштабирования используйте zoom.transform со заданным выбором или переходом; см. пример переходов при масштабировании. Метод zoom.event был удалён.

Для упрощения программированного масштабирования существуют несколько новых удобных методов на основе zoom.transform: zoom.translateBy, zoom.scaleBy и zoom.scaleTo. Также доступен новый API для описания преобразований масштабирования. Функции масштабирования больше не зависят от шкал, но вы можете использовать transform.rescaleX, transform.rescaleY, transform.invertX или transform.invertY для преобразования области значений шкалы. Вместо event.scale из версии 3.x теперь используется event.transform.k, а event.translate — event.transform.x и event.transform.y. Метод zoom.center был удалён в пользу программированного масштабирования.

Функция масштабирования теперь поддерживает простые ограничения на перемещение! Новый метод zoom.translateExtent позволяет определить область просмотра: текущая видимая область (область обзора, определяемая zoom.extent) всегда содержится в области перемещения. Метод zoom.size заменён на zoom.extent, а поведение по умолчанию теперь более разумное: по умолчанию оно использует область элемента-владельца функции масштабирования, а не жёстко заданные значения 960×500. (Это также улучшает путь по умолчанию при плавных переходах при масштабировании!)

Взаимодействие функции масштабирования также улучшено. Теперь она корректно обрабатывает одновременное прокручивание и перетаскивание, а также одновременное касание и использование мыши. Функция масштабирования теперь игнорирует события прокрутки на границах области масштабирования, позволяя вам прокручивать за пределы масштабируемой области. События zoomstart и zoomend были переименованы в start и end. По умолчанию функции масштабирования теперь игнорируют правые щелчки мыши, предназначенные для контекстного меню; используйте zoom.filter для управления событиями, которые игнорируются. Функция масштабирования также игнорирует эмулированные события мыши в iOS. Функция масштабирования теперь потребляет обработанные события, что упрощает её объединение с другими интерактивными функциями, такими как перетаскивание.

© 2010–2017 Michael Bostock
Licensed under the BSD License.
https://github.com/d3/d3/blob/master/CHANGES.md

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API