I’m using fabric JS free drawing mode to generate a bunch of hand-drawn SVGs. I then composite these onto a canvas. I convert them to a bitmap Image for rendering so that I can easily union all of the paths together (I opted to not use the globalCompositeOperation="xor"
trick to to merge paths without stacking opacities).
I would like to draw an SVG path around the border. SVG is preferred over a raster image so it can be easily animated. How can I generate this path? I have considered a few approaches:
- Vector math, for ex. paper JS does boolean operation. I’d like to avoid using a new library if possible though.
- An edge detection algorithm. This seems a bit non-trivial though, since there may be multiple “islands” (seen below).
Current code:
const renderMask = useCallback(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const paths = canvas.getObjects("path");
const oldMaskImg = canvas.getObjects("image").find((img) => img.name === "mask");
if (oldMaskImg) canvas.remove(oldMaskImg);
// Convert these paths to a bitmap, draw that instead
const offscreenCanvas = document.createElement("canvas");
offscreenCanvas.width = canvas.width!;
offscreenCanvas.height = canvas.height!;
const offscreenContext = offscreenCanvas.getContext("2d");
paths.forEach((path) => {
path.clone((pthClone) => {
pthClone.set({ stroke: 0.3 });
pthClone.render(offscreenContext);
});
path.set({ stroke: "rgba(0, 0, 0, 0)" }); // hide the path
});
// Convert every pixel in offscreenCanvas to 30% opacity
const imageData = offscreenContext.getImageData(
0,
0,
offscreenCanvas.width,
offscreenCanvas.height,
);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
if (data[i + 3] > 0) data[i + 3] = 0.3 * 255;
}
offscreenContext.putImageData(imageData, 0, 0);
const maskImg = new fabric.Image(offscreenCanvas, {
name: "mask", // Used to identify the mask for removing later
left: 0,
top: 0,
hasControls: false,
selectable: false,
});
canvas.add(maskImg);
canvas.renderAll();
}, []);
Is there an easier approach I’m missing? I have the full path (w/ brush radius) for each of the strokes.