A fork of 1wheel's original swoopyDrag.js
“The annotation layer is the most important thing we do” —Amanda Cox
swoopyDrag helps you hand place annotations on d3 graphics. It takes an array of objects representing annotations and turns them into lines and labels. Drag the text and control circles below to update the annotations array:
New! Click and drag from any point to create a new annotation with bootstrap.
The x and y functions are called on each annotation to determine its position. Setting
var swoopy = d3.swoopyDrag() .x(function(d){ return xScale(d.xVal) }) .y(function(d){ return yScale(d.yVal) }) .draggable(true) .annotations(annotations)
The shape of each annotation's line is determined by the path property, the text by the text property and the position of the text by the testOffset property. Currently only straight paths (paths of the form
The annotations are added to the page just like
var swoopySel = svg.append('g').call(swoopy)
After posititioning the labels, open the dev tools, run
Since each annotation's position is determined primarily by scales, lines and labels will still point to the correct position when the chart size changes. As the chart shrinks though, the annotations might overlap or cover up data points. To show fewer or differently positioned labels on mobile, you could create multiple annotation arrays for different screen sizes:
d3.swoopyDrag() .annotations(innerWidth < 800 ? mobileAnnotations : desktopAnnotations)
Alternatively if there's just one or two problematic annotations that only work above or below some sizes, you could add
d3.swoopyDrag() .annotations(annotations.filter(function(d){ return (typeof(d.minWidth) == 'undefined' || innerWidth > d.minWidth) && (typeof(d.maxWidth) == 'undefined' || innerWidth < d.maxWidth) }))
SVG has native support for arrowheads, but they can be a little fiddly to get working. First, add a
svg.append('marker') .attr('id', 'arrow') .attr('viewBox', '-10 -10 20 20') .attr('markerWidth', 20) .attr('markerHeight', 20) .attr('orient', 'auto') .append('path') .attr('d', 'M-6.75,-6.75 L 0,0 L -6.75,6.75')
Next, select paths in each annotation and set their
swoopySel.selectAll('path').attr('marker-end', 'url(#arrow)')
Multiline text can be added with d3-jetpack. Select all of the
swoopySel.selectAll('text') .each(function(d){ d3.select(this) .text('') //clear existing text .tspans(d3.wordwrap(d.text, 20)) //wrap after 20 char })
Since the annotations are made up of selectable
Bootstrap lets you add annotations by clicking and dragging.
var swoopyBootstrap = swoopy.bootstrap() .sel(swoopySel) // your d3 selection for swoopy annotations .labelAccessor(ƒ('species')) // this function sets label text .scale({x: yourXScale, y: yourYScale}) // your x and y scales d3.selectAll('rect') .data(yourData).enter() ... .call(swoopyBootstrap)
Minute by Minute Point Differentials
NBA Win/Loss Records
swoopyarrows creates fancier swoops, including circular and loopy arcs.
labella.js uses a force directed layout to position timeline labels with no overlap.
svg-crowbar lets you export a
ai2html illustrator script that creates responsive
ep = Math.max(1, Math.abs(600)) setWidth(curX + (isGrowing ? 1 : -1)*5) }) })() })