Is it possible to blur a closed path element that has a fill using paperjs? Would I have to create an SVG feGaussianBlur primitive for each path that I wanted to blur? How would that affect performance if there were a few hundred path elements on the canvas? I thought about making use of the shadowBlur with some color magic, where the shadow would match the selected path, but it's not quite the same effect. I do not want to use raster.
A trick could be to use the shadowBlur property of the item.
The only thing is that the shadow is only drawn if the item has a fill.
And we don't want the fill to be displayed but only the shadow.
So we could make clever usage of blend modes to hide the fill but not the shadow.
Here's a simple sketch demonstrating this.
new Path.Circle({
center: view.center,
radius: 50,
shadowColor: 'black',
shadowBlur: 20,
selected: true,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply',
});
This technique indeed seems to reach a limit when trying to stack items on top of each other.
We can prevent the multiply blend mode from "bleeding out" by wrapping the item into a group which has a blend mode of source-over. This has the effect to scope the children blend modes.
But there is still a trick to find to compose the items together.
Here's a sketch demonstrating where I stopped my investigation.
I'm quite sure that you can find a solution following this track.
function drawBlurryCircle(center, radius, blurAmount, color) {
const circle = new Path.Circle({
center,
radius,
shadowColor: color,
shadowBlur: blurAmount,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply'
});
const blurPlaceholder = circle
.clone()
.set({ shadowColor: null, fillColor: null })
.scale((circle.bounds.width + (blurAmount * 2)) / circle.bounds.width);
return new Group({
children: [circle, blurPlaceholder],
blendMode: 'source-over'
});
}
drawBlurryCircle(view.center, 50, 20, 'red');
drawBlurryCircle(view.center + 30, 40, 30, 'lime');
drawBlurryCircle(view.center + 60, 30, 30, 'blue');