Rough Notation
@rougher-stuff/rough-notation draws animated annotations around elements, text ranges, collections, and virtual targets. It also provides shared layers, connectors, and composed callouts.
Basic annotation
import { annotate } from '@rougher-stuff/rough-notation';
const annotation = annotate(target, {
type: 'underline',
color: '#cc5533',
seed: 42,
});
const controller = annotation.show({ autoplay: false });
controller.seek(0.4).play();
await controller.finished;Annotation types include underline, box, circle, highlight, strike-through, crossed-off, bracket, and sun-lines.
Targets and layers
Text effects preserve line fragments while shape effects use union bounds by default. Set rectMode to segments or union to override aggregation.
Layer surfaces are shared by root, placement, z-index, and name. They update on resize and scroll, ignore pointer events, and are reference-counted until the last annotation is removed.
Connectors
import { annotateArrow, annotateComment } from '@rougher-stuff/rough-notation/connectors';
const arrow = annotateArrow({
from: commentElement,
to: targetElement,
route: { type: 'swoop' },
end: { type: 'open-arrow' },
seed: 42,
});
arrow.show();
annotateComment({
comment: commentElement,
target: targetElement,
connector: { type: 'underline-arrow', route: { type: 'pigtail' } },
}).show();Callouts
annotateCallout() sequences an optional marker, target emphasis, connector, and content reveal on one shared timeline. Markers support asterisks, dots, and numbers.
Hide effects
annotation.hide({ effect: 'reverse-draw' });
annotation.hide({
effect: 'erase',
erase: { origin: 'top-left', width: 12, softness: 1, roughness: 2 },
});The eraser uses a cumulative rough sweep, so regions remain erased as its leading edge moves across the annotation.
See the package README for target styling, groups, and compatibility APIs.
