The answer was to challenge my own comfort zone. Allow myself to quote, myself:
I could do that with an HTML canvas, but I'm (possibly/probably wrongly) not going to use a canvas. I'm drawing divs dammit!
After all, if ever there was a time and place to learn how to do something completely new, isn't it a personal "fun" project? After a quick scan around the canvas-in-React landscape I settled upon React-konva, which has turned out to be completely awesome for my needs, as evidenced by the amount of code changes actually needed to go from my div-based way of drawing a line to the Konva way:
The old way:
const Line = styled('div')` position: absolute; border: 1px solid ${FrenzyColors.CYAN}; box-sizing: border-box; padding: 0; `; renderLine = (line, lineIndex) => { const [top, bottom] = minMax(line[2], line[4]); const [left, right] = minMax(line[1], line[3]); const height = (bottom - top) + 1; const width = (right - left) + 1; return <Line key={`line-${lineIndex}`} style={{ top: `${top}px`, left: `${left}px`, width: `${width}px`, height: `${height}px` }} />; }
The new way:
import { Line } from 'react-konva'; renderLine = (line, lineIndex) => { return ( <Line key={`line-${lineIndex}`} points={line.slice(1)} stroke={FrenzyColors.CYAN} strokeWidth={2} /> ); }
... and the jewel in the crown? Here's how simple it is to draw a closed polygon with the correct fill colour; by total fluke, my "model" for lines and polygons is virtually a one-to-one match with the Konva Line object, making this amazingly straightforward:
renderFilledArea = (filledPolygon) => { const { polygonLines, color } = filledPolygon; const flatPointsList = polygonLines.reduce((acc, l) => { const firstPair = l.slice(1, 3); return acc.concat(firstPair); }, []); return ( <Line points={flatPointsList} stroke={FrenzyColors.CYAN} strokeWidth={2} closed fill={color} /> ); }