Reputation: 26868
I want to clip my element using an SVG shape which is defined in the same HTML file (an inline SVG).
It works with clip-path
:
div {
width: 200px;
height: 200px;
background-color: red;
clip-path: url("#c");
}
<div>
<svg>
<defs>
<clippath id="c">
<circle cx="100" cy="100" r="50" />
</clippath>
</defs>
</svg>
</div>
But when using a mask, although this works fine in Firefox, it applies no masking in Chrome:
div {
width: 200px;
height: 200px;
background-color: red;
mask: url("#m");
}
<div>
<svg>
<defs>
<mask id="m">
<circle cx="100" cy="100" r="50" fill="white" />
</mask>
</defs>
</svg>
</div>
Searching around (example), it seems as though Chrome does not expect the mask to refer to a definition, but instead to an entire image. Is there a way to refer to an entire image if it has been inlined, though? Or, is there anything else I could do to apply a mask from an inline element?
Upvotes: 8
Views: 5934
Reputation: 136736
Here is an ugly js solution, which will
<mask>
,-webkit-mask-image
property.const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const mask = document.querySelector("mask");
svg.innerHTML = mask.innerHTML;
const markup = new XMLSerializer().serializeToString(svg);
const url = `data:image/svg+xml;charset=utf8,${ encodeURIComponent(markup) }`;
const div = document.getElementById('mask-me');
div.style.webkitMaskImage = `url('${ url }')`;
#mask-me {
width: 200px;
height: 200px;
background-color: red;
/**
This will be overidden by -webkit-mask-image even in Firefox.
We could have avoided it with a CSS variable, e.g
-webkit-mask-image: var(--workaround-url);
but Safari acts in all ways as if they did support inline SVG as mask,
when they don't.
So, for them, we have to use the workaround everywhere...
mask-image: url(#m);
**/
}
<div id="mask-me">
<svg>
<defs>
<mask id="m">
<circle cx="100" cy="100" r="50" fill="white" />
</mask>
</defs>
</svg>
</div>
For the ones needing the mask-mode: luminance
feature, you need to do it manually in the generated SVG image through a <feColorMatrix type="luminanceToAlpha"/>
:
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
// create a <filter><feColorMatrix type="luninanceToAllpha"/>
const alpha = document.createElementNS(svgNS, "feColorMatrix");
alpha.setAttribute("type", "luminanceToAlpha");
const filter = document.createElementNS(svgNS, "filter");
filter.setAttribute("id", "alpha");
filter.append(alpha);
svg.append(filter);
// apply the filter on all the <mask> content
const g = document.createElementNS(svgNS, "g");
g.setAttribute("filter", "url(#alpha)");
// luminanceToAlpha disregards semi-transparent pixels,
// so we add a black background to have no such thing
const bg = document.createElementNS(svgNS, "rect");
bg.setAttribute("width", "100%");
bg.setAttribute("height", "100%");
g.append(bg);
var mask = document.querySelector('mask').cloneNode(true);
g.append(...mask.children);
svg.append(g);
const markup = new XMLSerializer().serializeToString(svg);
const url = "data:image/svg+xml;charset=utf8," + encodeURIComponent(markup);
const div = document.getElementById("mask-me");
div.style.webkitMaskImage = `url('${ url }')`;
.border {
display: inline-block;
border: 1px solid;
}
#mask-me {
width: 300px;
height: 200px;
background-color: red;
/** Safari doesn't really support mask-image using inline SVG either...
mask-image: url(#m);
mask-mode: luminance;
**/
}
<div id="mask-me">
<svg width="0">
<defs>
<mask id="m">
<circle cx="100" cy="100" r="50" fill="white" />
<rect x="90" y="90" width="20" height="20" fill="black" />
</mask>
</defs>
</svg>
</div>
Upvotes: 5
Reputation: 101820
Last time I checked Chrome doesn't support the mask
property. It only supports mask-image
.
I imagine that is still the case.
Upvotes: -1