How do maskUnits & maskContentUnits attributes affect mask positioning? - html

I've read a bit of the spec for the SVG mask element but the sections on maskUnits and maskContentUnits aren't clear to me, and also how they affect each other isn't clear to me.
From the spec:
maskUnits = 'userSpaceOnUse': x, y, width and height represent values in the current user coordinate system in place at the time when the mask element is referenced.
maskUnits = 'boundingBox': x, y, width and height represent fractions or percentages of the object bounding box of the element to which the mask is applied.
maskContentUnits = 'userSpaceOnUse': The user coordinate system for the contents of the mask element is the current user coordinate system in place at the time when the mask element is referenced.
maskContentUnits = 'boundingBox': The coordinate system has its origin at the top left corner of the bounding box of the element to which the clipping path applies to and the same width and height of this bounding box.
I've tried editing the maskUnits example and maskContentUnits example on MDN but whenever I change anything something unexpected happens, like the whole element disappears or the mask doesn't seem to be applied.
The snippet below is a sandbox with a few examples of the confusing behavior. I'd expect all of the squares to look different but there are 2 pairs each that are identical, and one of the pairs look like no mask was applied at all:
* {
margin: 0;
padding: 0;
}
body {
padding: 20px 0 0 20px;
}
.hidden {
width: 0;
height: 0;
margin: 0;
}
svg {
width: 100px;
height: 100px;
margin: 0 20px 20px 0;
display: block;
}
p {
margin-bottom: 10px;
font-family: monospace;
}
<!-- mask definitions -->
<svg viewBox="0 0 100 100" class="hidden">
<mask
id="usou-usou"
maskUnits="userSpaceOnUse"
maskContentUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
<mask
id="usou-obb"
maskUnits="userSpaceOnUse"
maskContentUnits="objectBoundingBox"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
<mask
id="obb-usou"
maskUnits="objectBoundingBox"
maskContentUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
<mask
id="obb-obb"
maskUnits="objectBoundingBox"
maskContentUnits="objectBoundingBox"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
</svg>
<p>maskUnits = userSpaceOnUse &<br> maskContentUnits = userSpaceOnUse</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#usou-usou)"
/>
</svg>
<p>maskUnits = userSpaceOnUse &<br> maskContentUnits = objectBoundingBox</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#usou-obb)"
/>
</svg>
<p>maskUnits = objectBoundingBox &<br> maskContentUnits = userSpaceOnUse</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#obb-usou)"
/>
</svg>
<p>maskUnits = objectBoundingBox &<br> maskContentUnits = objectBoundingBox</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#obb-obb)"
/>
</svg>

We know what units are in ordinary life, there are inches, miles, kilometers etc.
One inch is not the same as one kilometre. If you draw a tiny picture, one inch across and you put a frame round it 2 inches across, the picture itself won't change if we make the frame 2 kilometres across. Equally, changing the picture size doesn't change how much is visible inside the picture frame unless the frame is too small for the picture.
maskUnits affect the units of the picture (mask) frame, maskContentUnits affect the units of the picture (mask).
objectBoundingBox units are defined such that 0 is the left side of the masked shape and 1 is the right side.
userSpaceOnUse units use the same co-ordinate system as the masked shape itself. If you mask a rect which extends from 50-100 then your mask ought to cover that area too if you want to mask the entire rect.
If you draw a circle with a radius of 100 kilometres centred 100 kilometres from the origin in both directions, then look at a square 1 millimetre across that starts at the origin, that square will have nothing drawn on it as everything is drawn outside that area.
We're masking
<rect x="0" y="0" width="100" height="100"/>
So our mask's x, y, width and height i.e.
<mask maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100"/>
need to cover the same area (or more) if we want to mask that shape and they do.
If we had maskUnits="objectBoundingBox" we'd need
<mask maskUnits="objectBoundingBox" x="0" y="0" width="1" height="1"/>
Using 100 for the width and height would make the mask 100 times the size it needs to be but other than wasting lots of memory, it has no visible effect.
maskContentUnits work the same for the mask's content i.e.
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
Either they need to be 0..1 for objectBoundingBox or 0..100 for the shape. Since they are far too big for objectBoundingBox the mask is all one colour as the shapes are outside the area you can see i.e. the area over the shape.
<!-- mask definitions -->
<svg viewBox="0 0 100 100" class="hidden">
<mask
id="usou-usou"
maskUnits="userSpaceOnUse"
maskContentUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
<mask
id="usou-obb"
maskUnits="userSpaceOnUse"
maskContentUnits="objectBoundingBox"
x="0"
y="0"
width="100"
height="100"
>
<rect x="0" y="0" width="1" height="1" fill="white" />
<!-- if we wanted the same mask as above it would be r="0.25" -->
<circle cx=".5" cy=".5" r=".1" fill="black" />
</mask>
<!-- have the mask cover only the top left quarter of the shape -->
<mask
id="obb-usou"
maskUnits="objectBoundingBox"
maskContentUnits="userSpaceOnUse"
x="0"
y="0"
width="0.5"
height="0.5"
>
<rect x="0" y="0" width="100" height="100" fill="white" />
<circle cx="50" cy="50" r="25" fill="black" />
</mask>
<!-- have the mask cover only the top left quarter of the shape -->
<mask
id="obb-obb"
maskUnits="objectBoundingBox"
maskContentUnits="objectBoundingBox"
x="0"
y="0"
width="0.5"
height="0.5"
>
<rect x="0" y="0" width="1" height="1" fill="white" />
<!-- if we wanted the same mask as above it would be r="0.25" -->
<circle cx=".5" cy=".5" r=".1" fill="black" />
</mask>
</svg>
<p>maskUnits = userSpaceOnUse &<br> maskContentUnits = userSpaceOnUse</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#usou-usou)"
/>
</svg>
<p>maskUnits = userSpaceOnUse &<br> maskContentUnits = objectBoundingBox</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#usou-obb)"
/>
</svg>
<p>maskUnits = objectBoundingBox &<br> maskContentUnits = userSpaceOnUse</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#obb-usou)"
/>
</svg>
<p>maskUnits = objectBoundingBox &<br> maskContentUnits = objectBoundingBox</p>
<svg viewBox="0 0 100 100">
<rect
x="0"
y="0"
width="100"
height="100"
mask="url(#obb-obb)"
/>
</svg>

Related

SVG file mask not being loaded with <use>

Let's say I have an SVG file test.svg
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<symbol id="testsvg">
<mask id="mask3" x="0" y="0" width="100" height="100" >
<rect x="0" y="0" width="100" height="50"
style="stroke:none; fill: #ffffff"/>
<rect x="0" y="50" width="100" height="50"
style="stroke:none; fill: #666666"/>
</mask>
<rect x="1" y="1" width="100" height="100"
style="stroke: none; fill: #0000ff; mask: url(#mask3)"/>
</symbol>
</defs>
</svg>
If i try to use it like this in test.html
<svg>
<use href="test.svg#testsvg"/>
</svg>
The mask is not working I get a blue rectangle only. Doing an inspect element in firefox shows that the mask element is missing inside the shadow root. (Why ?)
The same thing as a direct inline SVG works just fine as expected, (I get two rectangles of different colors)
test2.html
<svg xmlns="http://www.w3.org/2000/svg">
<mask id="mask3" x="0" y="0" width="100" height="100" >
<rect x="0" y="0" width="100" height="50"
style="stroke:none; fill: #ffffff"/>
<rect x="0" y="50" width="100" height="50"
style="stroke:none; fill: #666666"/>
</mask>
<rect x="1" y="1" width="100" height="100"
style="stroke: none; fill: #0000ff; mask: url(#mask3)"/>
</svg>
Why is this happening?
Note To reproduce the above behavior security.fileuri.strict_origin_policy should be set to false in about:config in Firefox so that local files may be loaded

Why is my svg mask partially transparent?

I have the following svg code:
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<mask id="mask" x="0" y="0" width="100" height="100">
<rect x="0" y="0" width="100" height="100" fill="rgb(20, 182, 197)" fill-opacity="100%"></rect>
<circle cx="50" cy="50" r="37.5"></circle>
</mask>
</defs>
<rect x="0" y="0" width="100" height="100" mask="url(#mask)" fill="rgb(20, 182, 197)" ></rect>
</svg>
It basically creates a rectangle with a cutout circle, but the part with the non cutout part is partially transparent.
How do i fix this?
With masks, the transparency is determined by the brightness. You are setting the rectangle's color to rgb(20, 182, 197), which has a 43% luminescence value, so you get 43% transparency. If you need the outer rectangle to be opaque, then you have to set its color to have 100% brightness (i.e. white).
So, change
<rect x="0" y="0" width="100" height="100" fill="rgb(20, 182, 197)" fill-opacity="100%">
to
<rect x="0" y="0" width="100" height="100" fill="white">

Fill svg logo letters with video background

I've used this way (you could see the codepen) to display image background in my logo letters. Now, i want to display a video instead of the image.
https://codepen.io/irawachaloco/pen/GJKLzy
<svg class='crop-shapes'>
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="100%" height="650">
<image class='twombly' xlink:href="http://gastv.mx/wp-content/uploads/2014/05/jumex.jpg" x="-30" y="-30"
width="380" height="267" />
</pattern>
</defs>
<circle class='circ' cx="50" cy="50" r="50" fill="url(#img1)" filter="url(#sparklin)" onmouseover="evt.target.setAttribute('opacity', '0.5');"
13
onmouseout="evt.target.setAttribute('opacity','1)');"/>
<rect x="110" y="0" width="100" height="100" stroke="black" fill="url(#img1)" filter="url(#sparklin)"/>
<polygon x="10" points="270,0 220,100 320,100" fill="url(#img1)" filter="url(#sparklin)"/>
</svg>
Any idea or example ? I'm a noob with svg practices..
Thanks a lot !

Keep <text> element scaled inside <svg> with viewBox

I'm trying to put some text as labels inside some scaled elements, and the text is too big to fit in the container. What can I do here?
<div class="t_container">
<div class="t_x" style="position: relative;">
<svg position="absolute" viewBox="0 0 6 1" preserveAspectRatio="none">
<g>
<rect x="0" y="0" width="1" height="0.4"><title>Nov-21</title></rect>
<text x="0.5" y="0.5" fill="red">A<text>
</g>
<rect x="1" y="0" width="1" height="1"><title>Nov-22</title></rect>
<rect x="2" y="0" width="1" height="1"><title>Nov-23</title></rect>
<rect x="3" y="0" width="1" height="1"><title>Nov-24</title></rect>
<rect x="4" y="0" width="1" height="1"><title>Nov-25</title></rect>
<rect x="5" y="0" width="1" height="1"><title>Nov-26</title></rect></svg>
</div>
Here is a codepen with the result.
You have a very small custom viewport="0 0 6 1" size. 6px - width, 1px - height, so the font can not be displayed with such parameters.
I increased the size of the viewBox 100 times viewBox="0 0 600 100"
Squares for clarity painted in different colors. You can change their coloring according to your choice.
The text is placed inside the squares. I hope that's exactly what you wanted when you used the command
<title> Nov-24 </ title> inside the squares.
But the command <title> in SVG is a system tooltip, the information from which appears when you hover the cursor.
The size of the tooltip and its font can not be changed, so I added in the squares more tags <text> ... </ text>, the parameters of which you can change.
<div class="t_container">
<div class="t_x" style="position: relative;">
<svg position="absolute" viewBox="0 0 600 100" >
<g>
<rect x="0" y="0" width="100" height="40"><title>Nov-21</title></rect>
<text x="35" y="75" font-size="36" fill="red">A</text>
</g>
<rect x="100" y="0" width="100" height="100" fill="orange">
<title>Nov-22</title></rect>
<text x="125" y="55" font-size="18" fill="white">Nov-22</text>
<rect x="200" y="0" width="100" height="100" fill="orangered">
<title>Nov-23</title></rect>
<text x="225" y="55" font-size="18" fill="white">Nov-23</text>
<rect x="300" y="0" width="100" height="100" fill="green">
<title>Nov-24</title></rect>
<text x="325" y="55" font-size="18" fill="white">Nov-24</text>
<rect x="400" y="0" width="100" height="100" fill="dodgerblue">
<title>Nov-25</title></rect>
<text x="425" y="55" font-size="18" fill="white">Nov-25</text>
<rect x="500" y="0" width="100" height="100" fill="yellowgreen">
<title>Nov-26</title></rect>
<text x="525" y="55" font-size="18" fill="white">Nov-26</text>
</svg>
</div>

Add a background image (.png) to a SVG circle shape

Is this possible? The following is what I tried but it completely fills the circle with black.
<svg id='vizMenu' width="700" height="660">
<defs>
<filter id="dropshadow" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="2"/>
<feOffset dx="0.5" dy="0.8" result="offsetblur"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<circle id='top' filter="url(#dropshadow)" cx="180" cy="120" r="80" stroke="#2E2E2E" stroke-width="2" fill="url('images/word-cloud.png')"/>
<circle id='bottom' filter="url(#dropshadow)" cx="500" cy="300" r="80" stroke="#2E2E2E" stroke-width="2" fill="url('images/word-cloud.png')"/>
<circle id='extra' filter="url(#dropshadow)" cx="180" cy="560" r="80" stroke="#2E2E2E" stroke-width="2" fill="#ffffff"/>
</svg>
An image fill for an svg element is achieved through SVG Patterns...
<svg width="700" height="660">
<defs>
<pattern id="image" x="0" y="0" patternUnits="userSpaceOnUse" height="1" width="1">
<image x="0" y="0" xlink:href="url.png"></image>
</pattern>
</defs>
<circle id='top' cx="180" cy="120" r="80" fill="url(#image)"/>
</svg>
Well, I couldn't make it work with the accepted answer. This is how I ended up doing it:
<svg width="100" height="100">
<defs>
<pattern id="image" patternUnits="userSpaceOnUse" height="100" width="100">
<image x="0" y="0" height="100" width="100" xlink:href="http://i.imgur.com/7Nlcay7.jpg"></image>
</pattern>
</defs>
<circle id='top' cx="50" cy="50" r="50" fill="url(#image)"/>
</svg>
If you want to customize the size, use this as a scale reference:
x = yourPreferredSize
<svg width=">2x" height=">2x">
<defs>
<pattern id="image" patternUnits="userSpaceOnUse" height=">2x" width=">2x">
<image x="0" y="0" height="2x" width="2x" xlink:href="http://i.imgur.com/7Nlcay7.jpg"></image>
</pattern>
</defs>
<circle id='top' cx="x" cy="x" r="x" fill="url(#image)"/>
</svg>
(This scale works for squared images)
Image repetition problem solved with proper explanation (Thanks to AmeliaBR)
TL;DR: The concept of objectBoundingBox and preserveAspectRatio are used!
<svg height = "10%" width = "10%">
<defs>
<pattern id = "attachedImage" height = "100%" width = "100%"
patternContentUnits = "objectBoundingBox">
<image xlink:href = "url.png" preserveAspectRatio = "none"
width = "1" height = "1"/>
</pattern>
</defs>
<circle cx = "50%" cy = "50%" r = "35%" fill = "url(#attachedImage)"/>
</svg>
I know this is an old question, but I used a filter to overlay the image. The above solution didn't work for me because of scaling and it seemed like the images was tiled. I used this instead, I hope it will help others as well.
<svg width="700" height="660">
<filter id="this_image" x="0%" y="0%" width="100%" height="100%">
<feImage xlink:href="test_image.png"/>
</filter>
<circle filter="url(#this_image)" cx="180" cy="120" r="80" />
</svg>
This is my solution, the differences are that this doesn't use the patternUnits="userSpaceOnUse" and that you specify the desired width and height of the image element.
<defs>
<pattern id="{some name}" x="0" y="0" width="1" height="1">
<image href="{image url}" x="0" y="0" width="{desired width}" height="{desired height}"></image>
</pattern>
</defs>