SVG is generating a viewbox even though there isn't one specified - svg

I'm trying to create an SVG play button that is only the size of the button itself, but it seems that there is some kind of viewbox being auto-generated. This phantom viewbox isn't even the same dimensions as the the play button and seems to be a 2:1 ratio.
#play-button {
border: 1px dashed gray;
}
<svg id="play-button">
<style type="text/css">
.st0{fill:none;stroke:#010101;stroke-miterlimit:10;}
.st1{fill:#010101;}
</style>
<circle id="button-border" class="st0" cx="30" cy="29.9" r="29.3"/>
<polygon id="play-triangle" class="st1" points="21.9,15.7 46.6,29.9 21.9,44.1 "/>
</svg>
How can I size the viewbox to the size of the SVG without specifying a viewBox or a height/width?

The outer SVG element is a replaced element. If you don't provide anything to go on for size you'll get the default width which is 300px and the default height which is 50% of the width so if you've not supplied a width value either, that ends up being 150px.
There are a number of ways to indicate the height and width you want. The most obvious would be height and width attributes or CSS properties of the same name but you can also use a viewBox attribute to define a width and height.

#RobertLongson's answer explains why this is happening. Here are some things you can do about it:
Your circle's r="29.3" does in effect "specify a height/width" in the markup. If you can put a value there, I would imagine you actually can the same value in the svg's width or height instead. Here's a different approach you could take with that in mind. It does require a viewbox but one that doesn't change: 0 0 100 100 just lets us use percentage values for the polygon's points. To calculate them, I did yourpointvalue/yourradius (e.g. 21.9/58.6). Using a border on the svg instead of a circle element makes this lighter weight and makes the markup easier to read. I've specified the width in the CSS, but it could also be inline; you could also only specify the height, or have it relative to a parent, etc etc.
(If you check the svg with your browser's inspector, you'll see it's the same width and height as the circle)
#play-button {
fill: #010101;
border: 1px solid #000;
border-radius: 50%;
display: block;
/* specify a width or a height either here or inline */
width: 58.6px;
}
<svg id="play-button" viewbox="0 0 100 100">
<polygon points="37.3,26.8 79.5,50 37.3,73.2" />
</svg>
If you really can't use viewbox, width, or height, and need to keep all that markup, you can achieve this with javascript. This is adapted from a solution by #Almis (but see #PaulLeBeau's take on the issue). The overflow: visible is necessary because the circle's stroke extends a little beyond the bounds of the svg. (Eventually we may be able to specify where a stroke is drawn, but not yet.)
var playButton = document.getElementById('play-button');
var boundingRect = document.getElementById('button-border').getBoundingClientRect();
playButton.style.height = boundingRect.height + 'px';
playButton.style.width = boundingRect.width + 'px';
#play-button {
border: 1px dashed gray;
overflow: visible; /* added */
}
<svg id="play-button">
<style type="text/css">
.st0 {
fill: none;
stroke: #010101;
stroke-miterlimit: 10;
}
.st1 {
fill: #010101;
}
</style>
<circle id="button-border" class="st0" cx="30" cy="29.9" r="29.3" />
<polygon id="play-triangle" class="st1" points="21.9,15.7 46.6,29.9 21.9,44.1 " />
</svg>

It looks like the answer is that the default size of an SVG is 300x150, which seems bizarre to me.
If you don't want that sizing (and you probably shouldn't rely on that default) you have to specify the size as detailed in this CSS Tricks article:
https://css-tricks.com/scale-svg/

Related

Specify baseline position in SVG

Is there a possibility, in SVG, to determine a vertical position as being the "baseline" for that SVG graphics?
Context: we include a lot of inline SVG in tasks I prepare for students (see the attached screenshot, in which the circled 2 and 3 are actually SVG data). This text with inline SVG is published as HTML and in LaTeX. I'd like to find a way to include in my SVG files some marker such that later I don't have to manually specify a vertical offset for each graphic files to be perfectly aligned.
In this example, for instance, the bottom of the "2" within the circle should be determined as baseline, such that it can be automatically aligned with the bottom the of other characters that have no descender.
No, there is no such marker. The best you can do for a workaround is probably this: Set the bottom of the viewBox such that it represents the baseline. Then, if you globally set overflow: visible for all of your SVG icons, it doesn't matter if you have grafical content outside the viewBox, especially below the baseline.
p {
font-size: 40px;
}
svg {
width: 1em;
overflow: visible;
}
circle {
fill: none;
stroke: black;
}
text {
font-size: 14px;
}
<p>Example<svg viewBox="0 0 20 15">
<circle r="8.5" cx="10" cy="10" />
<text x="6" y="15">1</text>
</svg>text</p>

css solution for responsive SVG chart (polyline coordinates)

I'm using javascript to generate the points coordinates of a polyline element inside a line chart. The chart must have a fixed height (210px) and a responsive width (always 100% of its parent div).
Problem is the points coordinates of the polyline don't change when resizing the window.
Is there a way to set the coordinates of the polyline as percentage of its parent div so that the horizontal coordinates change when resizing the window ?
I know I could use javascript to listen for window resize and recalculate the points coordinates of the polyline but it seems somewhat overkill so I was hoping there was a lighter, pure CSS solution.
Fiddle: https://jsfiddle.net/Hal_9100/1cnq389g/
You need to add a viewBox to your SVG. If you want the graph to stretch horizontally, to fit the box, then you'll also want to add a suitable preserveAspectRatio value.
* {padding: 0; margin: 0;}
#container {
width: 60%; height: 210px;
background: #fff;
}
svg {
width: 100%; height: 210px;
position: relative;
border: 2px solid black;
}
<div id="container">
<svg viewBox="0 0 450 210" preserveAspectRatio="none">
<polyline id="myLine" fill="none" stroke="#2681DC" stroke-width="2" points="0,210 50,67 100,174 150,198 200,202 250,190 300,205 350,207 400,198 450,19 "></polyline>
</svg>
</div>
https://jsfiddle.net/1cnq389g/2/

svg with "non-scaling-stroke" ignores viewbox and uses its viewport

I'm working with an SVG that I don't want the stroke of <circle> to scale when resized. What happens when I add vector-effect="non-scaling-stroke" to the <circle> is that now the circumference becomes based on the viewport and not the original viewBox. This is a problem because when using stroke-dasharray for an animation, it's referencing the wrong circumference.
The following snippet shows a stroke-dasharray set to the circumference of two circles of the same size and the right circle showing the problem (using the viewport; effectively doubling its circumference):
svg {
width: 400px;
height: auto;
fill: none;
stroke: #000;
stroke-width: 6;
stroke-dasharray: 252
}
<body>
<svg viewBox="0 0 200 100">
<circle cx="50" cy="50" r="40" />
<circle cx="150" cy="50" r="40" vector-effect="non-scaling-stroke" />
</svg>
</body>
Is there any way to tell the <circle> to respect the viewBox and not the viewport?
This is what happens when you specify non-scaling stroke: it's "not-scaling" the "dash" in the stroke as well as the stroke itself. If you had a normal stroke dash array (rather than using it as an animation hack) - this is the behavior you would want :) There is currently no way to specify a different coordinate system just for the dash-array calculation, so javascript is your friend.

prevent path deformation made by preserveAspectRatio="none"

I'd like to prevent <path> elements from stretching caused bypreserveAspectRatio="none" on <svg> tag.
I've found nearly perfect solution adding this attribute to <path> elements:
vector-effect="non-scaling-stroke"
But I need them to be scalable with transform="scale()" which is also disabled or more precisely their stroke-width are fixed and not scalable. So, my question is:
Is there any way to prevent stretching but not zooming?
Ok, to make it more clear: https://codepen.io/lukydorny/pen/aNYOdW
I would like to shape a path by different viewBox width and height (2nd image) but I need horizontal line to be of same width as vertical(3rd image). And then I wish I was able to scale it exactly the same way as original path(4th image) which is disabled as you can see in the last image.
Is there any way how to do it?
Yes, you can achieve this simply by defining the SVG element like this:
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="100%"
height="100%"
viewBox="0 0 1920 1080"
style="display:block">
... your SVG paths code here
</svg>
Notice the "viewBox" size should be the size of your "svg document" (maximum x and maximum y values).
This preserves the aspect ratio you define; however, be aware that your paths created should comply with the size you define, else it may cause problems.
To "zoom" the whole graphic, simply change with width & height of the SVG element, or its parent element, or use "scale()" inside the SVG, or with JavaScript as you normally would.
Would this option work?
svg {
border: solid 1px
}
<div title='original path' style='float:left;'>
<svg width=192px height=54px viewBox='0 0 200 200'>
<path style="stroke-linejoin: round; stroke-linecap: round; opacity: 1; stroke: rgb(50, 122, 97); stroke-width: 8px; fill: none;" d="m30 68.709l5,0,4.111,0,3.444,0,3.555,0,4.111,0.071,5,0.214,5.222,0.428,5.222,0.571,5,0.642,4.778,0.571,4.778,0.428,4.778,0.214,5.111,0.071,4.778,0,4.333,0,3.667,0,3.444,0,3.333,0,3.222,0,3,0,2.556,0,2.111,-0.071,1.889,-0.214,1.778,-0.499,1.778,-0.785,1.445,-1.07,1.111,-1.284,0.778,-1.498,0.556,-2.069,0.444,-2.64,0.222,-3.211,0.111,-2.997,0,-2.569,0,-1.998,0,-1.998,0,-2.283,0,-2.64,0,-2.497,0,-2.069,0,-1.569,-0.556,-1.284,-1.556,-1.07,-3,-0.785,-3.778,-0.499,-4.222,-0.214,-4.222,-0.071,-4.889,0,-5.333,0,-6,0,-5.778,-0.071,-5.778,-0.214,-5.556,-0.428,-5.556,-0.571,-5.889,-0.571,-5.889,-0.428,-5.778,-0.214,-4.445,-0.143,-3.333,-0.143,-2.444,-0.214,-2.333,-0.214,-2.333,-0.214,-2.222,-0.214,-2,-0.214,-1.778,-0.214,-1.667,-0.214,-1.778,-0.071,-1.778,0.357,-1.667,1.926,-1.222,3.781,-0.778,5.494,-0.333,5.28,-0.111,4.495,0,3.568,0,3.496,0,4.21,0,4.923,0,5.494,0,4.78,0,3.996,0,3.282,0,3.14,0,3.354,0,3.496,0.111,3.568,0.333,2.854,0.667,2.069,1.111,1.427,1.445,1.213,1.778,0.999,1.778,0.713,1.889,0.499,1.889,0.499,2.111,0.499,2.889,0.571,4.111,0.571,5.778,0.713,6.778,0.713,7.333,0.642,7,0.428,6.333,0.214,5.778,0.071,5.667,0,6.111,0,7.111,0,8.111,0,8.556,0,7.333,0,5.667,-0.071,4.444,-0.143,4.333,-0.285,4.556,-0.285,4.667,-0.285,4.556,-0.143,4.333,-0.143,4,-0.143,3.333,-0.214,2,-0.143,0,0"></path>
</svg>
</div>
<div title='original path' style='float:left;'>
<svg width=192px height=108px viewBox='0 0 200 200'>
<path style="stroke-linejoin: round; stroke-linecap: round; opacity: 1; stroke: rgb(50, 122, 97); stroke-width: 8px; fill: none;" d="m30 68.709l5,0,4.111,0,3.444,0,3.555,0,4.111,0.071,5,0.214,5.222,0.428,5.222,0.571,5,0.642,4.778,0.571,4.778,0.428,4.778,0.214,5.111,0.071,4.778,0,4.333,0,3.667,0,3.444,0,3.333,0,3.222,0,3,0,2.556,0,2.111,-0.071,1.889,-0.214,1.778,-0.499,1.778,-0.785,1.445,-1.07,1.111,-1.284,0.778,-1.498,0.556,-2.069,0.444,-2.64,0.222,-3.211,0.111,-2.997,0,-2.569,0,-1.998,0,-1.998,0,-2.283,0,-2.64,0,-2.497,0,-2.069,0,-1.569,-0.556,-1.284,-1.556,-1.07,-3,-0.785,-3.778,-0.499,-4.222,-0.214,-4.222,-0.071,-4.889,0,-5.333,0,-6,0,-5.778,-0.071,-5.778,-0.214,-5.556,-0.428,-5.556,-0.571,-5.889,-0.571,-5.889,-0.428,-5.778,-0.214,-4.445,-0.143,-3.333,-0.143,-2.444,-0.214,-2.333,-0.214,-2.333,-0.214,-2.222,-0.214,-2,-0.214,-1.778,-0.214,-1.667,-0.214,-1.778,-0.071,-1.778,0.357,-1.667,1.926,-1.222,3.781,-0.778,5.494,-0.333,5.28,-0.111,4.495,0,3.568,0,3.496,0,4.21,0,4.923,0,5.494,0,4.78,0,3.996,0,3.282,0,3.14,0,3.354,0,3.496,0.111,3.568,0.333,2.854,0.667,2.069,1.111,1.427,1.445,1.213,1.778,0.999,1.778,0.713,1.889,0.499,1.889,0.499,2.111,0.499,2.889,0.571,4.111,0.571,5.778,0.713,6.778,0.713,7.333,0.642,7,0.428,6.333,0.214,5.778,0.071,5.667,0,6.111,0,7.111,0,8.111,0,8.556,0,7.333,0,5.667,-0.071,4.444,-0.143,4.333,-0.285,4.556,-0.285,4.667,-0.285,4.556,-0.143,4.333,-0.143,4,-0.143,3.333,-0.214,2,-0.143,0,0"></path>
</svg>
</div>

Auto height for a foreignObject in SVG

I have, in my SVG, a foreignObject which contains a p element. I want the height of this structure to be adapted to the height of my text.
The p element is already adapted : I've done nothing for that.
But I have troubles for the foreignObject. If I remove the field height, it doesn't work. height:auto doesn't work either.
Since there is no real use of scaling up and down the foreignObject itself, then you can set both foreignObject height and width to 1, and via CSS set foreignObject { overflow: visible; } to make its content visible whatever it is and do whatever you need to do it with it.
You can set height of the foreignObject element in em units, maybe that could help?
Right now the width and height attributes of a foreignObject are required, and must have values > 0, otherwise the element will not be rendered.
Update: An alternative is to just set the dimensions of the foreignObject to 100%, and use the fact that the foreignObject has a transparent background per default. Since other elements in svg are laid out in an absolute manner anyway they don't depend on the foreignObject size.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
p { position: absolute; top: 50%; left: 50%; width: 100px; }
</style>
<circle cx="50%" cy="50%" r="25%" fill="lightblue"/>
<foreignObject width="100%" height="100%">
<p xmlns="http://www.w3.org/1999/xhtml">
some wrapped text...
some wrapped text...
some wrapped text...
some wrapped text...
</p>
</foreignObject>
</svg>

Resources