UNPKG

d3-cloud

Version:

Generate word clouds in JavaScript.

118 lines (105 loc) • 6.68 kB
# Changelog All notable changes to this project will be documented in this file. This changelog currently documents the 2.x line. The previous stable 1.x release was `v1.2.9`. ## [2.0.0] - 2026-10-30 See the [1.x to 2.x migration guide](./MIGRATING.md) before upgrading. Dependencies constrained to 1.x continue to use the old API. ### Breaking - Set the minimum Node.js version to 20, matching the CI test matrix. - Switched the package to ESM-only publishing and removed the old Browserify / CommonJS distribution shape. - Removed the UMD/script-tag build, the `d3.layout.cloud` global, and the `d3-dispatch` dependency. Browser scripts must use ESM imports. - Changed the default rotation from random multiples of 30 degrees to zero, and the default font size from `sqrt(value)` to 1. Set styling explicitly. - Input word objects are no longer mutated. Placement returns separate plain words, and callers control the word ordering. - Custom data is carried in a single `datum` option, stored by reference on sprites and placed words. Other option fields are ignored rather than copied onto sprites and results, so built-in field names are no longer reserved. - Replaced the old event-driven factory API (`start()`, `stop()`, `on()`, `words()`, `timeInterval()`, accessor-based text/font configuration) with the class-based `CloudLayout` / `CloudSprite` API. - Replaced the old `spiral()` API with `strategy()`. - Moved source files under `src/` and renamed the browser bundle to `build/d3-cloud.js`. - Changed placement configuration so `size([width, height])` defines the centered layout box, while `overflow(true | false)` controls whether placement may extend beyond that box. - Text padding now uses round stroke joins instead of the canvas default miter joins, so padding extends exactly `padding` pixels from the glyph outline rather than up to ten times that at sharp corners. Padded masks, and layouts that depend on them, differ from 1.x. - The rectangular strategy now visits every point of a 4-pixel grid in rings shaped to the aspect ratio, instead of stepping four pixels times the aspect ratio along the longer axis. Rectangular layouts differ from 1.x, and pack densely at any aspect ratio. - Both built-in spirals stretch to the aspect ratio only up to 64:1. Beyond that they barely moved along the shorter axis, so a search starting in a blocked column or row could take minutes and then fail. Layouts with more extreme aspect ratios differ from 1.x. - Invalid arguments throw instead of being silently replaced: non-finite coordinates, sizes, rotations, padding, image dimensions and strategy candidates; negative padding; `size()` dimensions outside 0–16,777,216; `blockSize()` outside 1–4096; a `random()` that is not a function or returns anything but a number in `[0, 1)`; fonts the canvas rejects; canvases without a 2d context; and canvases whose `getImageData()` returns more than one pixel per canvas unit, as some old high-density WebKit canvases did. ### Added - Added `CloudLayout.getSprite(...)` for explicit sprite preparation and `place()` for caller-controlled placement. Sprites are immutable masks with no position, so one sprite can be placed any number of times; they can only be created by `getSprite()`. - Added built-in placement strategies including `noneStrategy` for single-attempt placement. - Added `erase()` for erasing a word returned by `place()`. - Added image sprite extraction from alpha masks, including optional image resizing with `options.width` and `options.height`. Loaded images without intrinsic dimensions, such as SVGs with only a `viewBox`, are drawn at an explicit width and height. - Added sparse packed block placement for effectively unbounded layouts. - Added browser examples for ESM loading, image sprites, and masked layout. - Added a `node:test` regression suite and a standalone benchmark script. ### Changed - Placement first tries the seed, then searches from the nearest position where the sprite could touch the cloud (and, with `overflow(false)`, fit in the box), skipping positions where it could not without a collision check. Distant seeds cost no extra time, and small clouds are not stepped over. - Search limits scale with the number of positions where the sprite could be placed, rather than the board diagonal, and apply equally to built-in and custom strategies. Strategies that repeat points still stop; one that moves away along one axis and never returns stops after a run proportional to the area it could have searched. - Placement uses a private copy of each sprite's mask and accepts only sprites created by `getSprite()`, so changing or forging a sprite cannot corrupt a layout. - Text rasters are sized from measured ink bounds, including italic overhang and accents, and redrawn larger if the ink reaches the raster edge, as some canvases under-report ink bounds. Text is rasterized with `textAlign = "center"`, matching how placed words should be rendered. - Sprite geometry is trimmed to occupied pixels, independently of the packed row alignment, so bounded layouts do not reject words because of raster slack. Sprite extraction reads back only the raster and stores masks as `Uint32Array`s. - Layouts may share one scratch canvas. A failed draw or readback, for example from a tainted canvas, makes every layout using it ask its factory for a canvas again; a rejected font does not. - `getSprite()` throws if called while a sprite is being drawn to the same canvas, and `clear()` and `blockSize()` throw while `place()` is searching, rather than corrupting masks or occupancy. - The package includes TypeScript declarations, for both the package entry and `build/d3-cloud.js`. `datum` is typed generically and inferred from `getSprite()` options. Image sources may be images, videos, canvases, or `ImageBitmap`s, including node-canvas, and placed words keep the source type, so `word.image` can be drawn directly. - Documented the rendering contract: image coordinates mark the sprite center, and text is rendered from `x` and `y` with a centered horizontal anchor. - Rewrote the examples for the ESM build. ### Known limitations - The archimedean strategy samples points about a tenth of the radius apart and stretches its spiral to the aspect ratio, so in fixed boxes at extreme aspect ratios (around 20:1 and beyond) it places fewer words than at moderate ratios. Use the rectangular strategy for such boxes. - The rectangular strategy samples a finer grid than in 1.x, so it can be slower in wide layouts, in exchange for denser packing.