I am working on a web project where I display dynamically rendered items which I would like to display in a very specific way: They should be masked with a black and white image and then be applied an SVG filter.
The mask images are highly contrasted black and white jpgs which are created in the backend from the images which the users upload. This is an example of such an item:
svg {
width: 100px;
height: 100px;
filter: url("#outline");
padding: 1px;
transition: all 800ms;
}
svg:hover {
transform: rotate(90deg);
}
.wrapper {
position: absolute;
padding: 10px;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
}
<div class="wrapper">
<svg
filter="url(#outline)"
width="100%"
height="100%"
>
<defs>
<mask id="mask">
<image
x="0"
y="0"
width="100%"
height="100%"
xlink:href="https://i.ibb.co/pL9J807/test.png"
/>
</mask>
<filter id="outline" color-interpolation-filters="sRGB">
<feDropShadow
dx="0"
dy="0"
stdDeviation="1.25"
in="SourceGraphic"
flood-color="white"
result="blur"
flood-opacity="10"
></feDropShadow>
<feColorMatrix
mode="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 55 -5"
in="blur"
></feColorMatrix>
</filter>
</defs>
<rect
width="100%"
height="100%"
mask="url('#mask')"
fill="red"
/>
</svg>
</div>
There is additionally an intro animation, which makes the items rotate. It works perfectly fine in Chrome. In Safari, however, the masking creates performance issues, making the masked elements jump and rotate in an unexpected way even after the intro animation is done. Apparently, Safari does not do too well with SVG masks, hence I am looking for an alternative to mask single-colored divs/rects with black and white images.
Is there a way to use CSS masks that also works with black/white as input instead of black/transparent? Or is there maybe something about the SVG setup that could be changed in order to icrease performance? I also included the SVG filter in the code snippet although the performance issues arise even when it is removed as it might be relevant when thinking about alternatives.
I am grateful for any hints!
2
Answers
As already implied by Michael Mullany
SVG filters are notorious for negatively affecting rendering performance.
Workarounds to mitigate jittery animations/transitions
transition: transform 800ms;
Maybe vectorize mask images provided by users?
Applying a
<mask>
is usually more expensive than applying a<clip-path>
since masks also support semi-transparent areas introducing a more complex alpha transparency rendering context.Provided all user uploads are "high-contrast" or perfect black and white images (… or will be converted to these) – you might even try to vectorize these files via something like potrace (available for pretty much any language – here’s just a javaScript example).
Trace/vectorize by potrace
Ideally you could convert the uploaded images server-side.
Provided, all images are similarly simple and clean as the example png, this might be a valid alternative.
The advantage of this approach – you’ll get a svg already including the desired transparency. So there’s no need to clip or mask anything.
Besides you could apply regular fill and stroke properties – no need to emulate fills or strokes with a filter.
You can move the mask into the filter using feComposite/in and try to animate it there. Now it used to be that Safari didn’t support feComposite when it was applied via CSS (rather than an SVG attribute) – but hopefully that has changed.