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

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>

Related

Rotate svg in a div container

I created an svg. Since I need to rotate it by 45degree, I wrapped it in a div that I transform with a rotation.
It works but the svg overflows the window.
Here the code:
.container {
background-color: tomato;
transform: rotate(45deg);
width: max-content;
}
<div class="container">
<svg width="200" height="200" x="0" y="0">
<rect x="0" y="0" width="100" height="100" fill="pink" stroke="black" />
<rect x="100" y="0" width="100" height="100" fill="white" stroke="black" />
<rect x="0" y="100" width="100" height="100" fill="white" stroke="black" />
<rect x="100" y="100" width="100" height="100" fill="white" stroke="black" />
</svg>
</div>
This is what I would like to obtain:
I would rotate just svg, because you don't need to rotate whole container. You still need add some padding because, when you rotate square it is longer left to right than before.
.container {
width: max-content;
padding: 50px;
}
svg {
transform: rotate(45deg);
}
<div class="container">
<svg width="200" height="200" x="0" y="0">
<rect x="0" y="0" width="100" height="100" fill="pink" stroke="black" />
<rect x="100" y="0" width="100" height="100" fill="white" stroke="black" />
<rect x="0" y="100" width="100" height="100" fill="white" stroke="black" />
<rect x="100" y="100" width="100" height="100" fill="white" stroke="black" />
</svg>
</div>

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

How to make svg size to fit content text

Please check my code link
<div class="suj_content">
<header class="suj_content_hd">
<div id="suj_content_hd_ytb"><iframe class="suj_content_hd_ytb" src="https://www.youtube.com/embed/kOc6ME2J_Us?mute=1&loop=1&playlist=kOc6ME2J_Us&autoplay=1&showinfo=0&controls=0" width="100%" height="100%" frameborder="0"></iframe></div>
<h2>
<svg id="suj_content_svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100% 100%" preserveAspectRatio="xMidYMid slice">
<defs>
<mask id="mask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%"></rect>
<text>
<tspan x="0" dy="33.333333333333%" alignment-baseline="middle" text-anchor="start">mittel</tspan><tspan x="0" dy="23.222222222222%" alignment-baseline="middle" text-anchor="start">stand</tspan><tspan x="0" dy="23.222222222222%" alignment-baseline="middle" text-anchor="start">digital</tspan> </text>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%"></rect>
</svg>
</h2>
</header>
How can I set the width and height of SVG tag to fit its content (the tspan tags). The number of tspan tags is not fixed.
Thanks
First: you are using an invalid viewBox attribute. No percentages are allowed. The value of the viewBox is fromX fromY width height.
I am using a viewBox where the width is 41 - the width of the bounding box of the text.
Second: I suspect you intend to cut the text from the last rect. In this case you need the text to be white.
console.log(t.getBBox())
text{font-size:16px;}
<svg id="suj_content_svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 41 50" preserveAspectRatio="xMidYMid slice">
<defs>
<mask id="mask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%"></rect>
<text fill="white" id="t">
<tspan x="0" dy="25%" dominant-baseline="middle" text-anchor="start">mittel</tspan>
<tspan x="0" dy="25%" dominant-baseline="middle" text-anchor="start">stand</tspan>
<tspan x="0" dy="25%" dominant-baseline="middle" text-anchor="start">digital</tspan>
</text>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" mask="url(#mask)"></rect>
</svg>

How do maskUnits & maskContentUnits attributes affect mask positioning?

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>

Why do my svg look so bad?

My svg looks very bad in Google Chrome and Firefox too, the Svg borders have poor quality:
Meanwhile, in Illustrator the svg looks awesome:
I have saved the .svg file with this configuration:
What is happened?
If your SVG has a lot of horizontal and/or vertical lines, you can improve its appearance by aligning the coordinates to the pixel grid. I'll give you an example:
Here are three SVG images made of rounded rectangles. (The source code for these images is pasted below.)
In (A), the rectangle coordinates aren't aligned to the pixel grid at all. As a result, some of the lines are clear and sharp while others are fuzzy and a bit darker.
In (B), the rectangle coordinates are snapped to integer values, giving them a uniform appearance. However, they all look fuzzy now, because the antialiasing spreads each line across a width of two pixels.
In (C), the coordinates are snapped to integer values and given an additional offset of 0.5 pixels in the x and y directions. You should be able to see a definite improvement here.
If you're working in Illustrator, try viewing your artwork at 100% in "Pixel Preview" mode.
I would also recommend not using stroke widths smaller than 1 pixel. If you want to simulate thinner lines, try reducing the opacity instead.
<svg width="200" height="150" viewBox="0 0 200 150">
<!-- (Original drawing) -->
<rect x="0" y="0" width="200" height="150" fill="#47f" stroke="none" />
<g fill="none" stroke="#fff" stroke-width="1.2">
<rect x="20.1" y="20.1" width="160" height="110" rx="50" ry="50"/>
<rect x="25.3071" y="25.3071" width="149.5857" height="99.5857" rx="44.7929" ry="44.7929"/>
<rect x="30.5143" y="30.5143" width="139.1714" height="89.1714" rx="39.5857" ry="39.5857"/>
<rect x="35.7215" y="35.7215" width="128.7571" height="78.7571" rx="34.3785" ry="34.3785"/>
<rect x="40.9286" y="40.9286" width="118.3428" height="68.3428" rx="29.1714" ry="29.1714"/>
</g>
<text x="100" y="80" text-anchor="middle" font-family="sans-serif" font-size="20" fill="#fff">(A)</text>
</svg>
<svg width="200" height="150" viewBox="0 0 200 150">
<!-- (Lines snapped to integer coordinates) -->
<rect x="0" y="0" width="200" height="150" fill="#47f" stroke="none" />
<g fill="none" stroke="#fff" stroke-width="1.2">
<rect x="20" y="20" width="160" height="110" rx="50" ry="50"/>
<rect x="25" y="25" width="150" height="100" rx="45" ry="45"/>
<rect x="30" y="30" width="140" height="90" rx="40" ry="40"/>
<rect x="35" y="35" width="130" height="80" rx="35" ry="35"/>
<rect x="40" y="40" width="120" height="70" rx="30" ry="30"/>
</g>
<text x="100" y="80" text-anchor="middle" font-family="sans-serif" font-size="20" fill="#fff">(B)</text>
</svg>
<svg width="200" height="150" viewBox="0 0 200 150">
<text x="100" y="80" text-anchor="middle" font-family="sans-serif" font-size="20" fill="#fff">(A)</text>
<!-- (Lines snapped to integer coordinates with 0.5px offset) -->
<rect x="0" y="0" width="200" height="150" fill="#47f" stroke="none" />
<g fill="none" stroke="#fff" stroke-width="1.2">
<rect x="20.5" y="20.5" width="160" height="110" rx="50" ry="50"/>
<rect x="25.5" y="25.5" width="150" height="100" rx="45" ry="45"/>
<rect x="30.5" y="30.5" width="140" height="90" rx="40" ry="40"/>
<rect x="35.5" y="35.5" width="130" height="80" rx="35" ry="35"/>
<rect x="40.5" y="40.5" width="120" height="70" rx="30" ry="30"/>
</g>
<text x="100" y="80" text-anchor="middle" font-family="sans-serif" font-size="20" fill="#fff">(C)</text>
</svg>
In your "bad" example, the SVG has been reduced to roughly half size. That means some of the lines that are approx 1 pixel thick in your "good" example are now only around 0.5 pixels thick. That doesn't give the anti-aliasing routines in the SVG renderer much to play with. Try making the stroke widths thicker.
You should get better results then.