How do I get svg vmin right? - svg

According to the standards I'm reading -- for example https://www.w3.org/TR/css-values-4/#viewport-relative-lengths -- a vmin unit should be 1% of the smallest dimension of the containing viewport.
Going for a minimal example illustrating my dilemma, this is square in my current instance of chrome:
<svg><rect height="30vmin" width="30vmin" fill="red">
But this is not:
<svg><rect height="50vmin" width="50vmin" fill="red">
Playing with variations on this theme (closing tags, adding width and height to the svg element, etc.) suggests that the rect is not using the svg viewport as its reference, but instead is using some containing browser context as its reference viewport.
So, my question is: how do I specify to the browser that I want vmin units to refer to the innermost containing svg viewport? (Specifically when working with svg elements embedded in html documents.)

Browser support for those units that were added in CSS3 may still be spotty. I haven't checked recently.
But the rule is that these units are resolved relative to the whole document. So in a browser, that will be the whole browser window.
This SVGWG issue may help clarify things.
https://github.com/w3c/svgwg/issues/207
how do I specify to the browser that I want vmin units to refer to the innermost containing svg viewport?
You can use percentage values for coordinates,
<rect height="50%" width="50%" fill="red">
However in SVG, percentage values are always relative to their associated axis. So percentage width values are relative to the X axis, and percentage height values are relative to the Y axis.
Alternatively you could use a suitable viewBox and appropriate coordinate values relative to that viewBox. For example, if your viewBox has a width and height of 100:
viewBox="0 0 100 100"
All coordinates values in the SVG would effectively be percentage values. However the same axis rule applies as described above.
svg {
width: 200px;
background-color: linen;
}
<svg viewBox="0 0 100 100">
<!-- rectnagle 50% x 33.3% -->
<rect width="50" height="33.3"/>
</svg>

Related

Why are the SVG Text elements too high?

I noticed the the root coordinates for a text element are not at the top left corner like a rect element:
Is there a way to set it such that when a text element is at (0,0), it fits inside the parent element?
If I understood you well, you can use this:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline
A) Chromium browsers
svg {
dominant-baseline: hanging;
}
https://jsfiddle.net/e7vc4bqj/
B) Chromium and Firefox
.text {
dominant-baseline: hanging;
}
https://jsfiddle.net/3zskd148/
SVG text coordinates are used to define its left bottom corner by default:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor
Hope this help :)
Why are the SVG Text elements too high?
The x and y coordinates of a <text> element specify the start of the baseline of the text. This makes complete sense. You wouldn't want it to be the top left of the first character - because it would then be a difficult job to get text of different sizes and styles to line up.
There is no global option in SVG that changes that behaviour. However see below for alternatives)
Is there a way to set it such that when a text element is at (0,0), it fits inside the parent element?
Normally you would just adjust the y coordinate based on the font size.
However there are a couple of alternatives you can use:
One is the xxx-baseline properties (as #gengns has pointed out), that can alter how the character glyphs are positioned relative to the baseline. Note however, that those attributes are not entirely reliable, due to mixed browser support. Plus they depend on the font containing the correct data tables. Not all fonts have those tables.
A better option IMO is to use the dy attribute. This adds a relative offset to the text position. Meaning the text is actually positioned at (x, y + dy). And it is supported by all browsers.
<svg width="200" height="150">
<rect x="0" y="0" width="200" height="150" fill="skyblue"/>
<text x="0" y="0"
font-size="25px" dy="1em">asd</text>
</svg>

SVG : line with y attribute expressed in percentage units does not get affected by viewBox

It seems that specifying line coordinates of an SVG line in percentages makes that coordinate exist in viewport coordinate system instead of the user coordinate system established by a viewBox. To me that sounds strange, especially after reading the specs.
In the example below, the green line is defined by user space coordinates while the blue line's y coordinate is in percentage units (50%). When the button is clicked, the viewBox is applied - the green line is scaled properly while the blue line is not ... What is going on there?
The spec says:
For any y-coordinate value or height value expressed as a percentage of the SVG viewport, the value to use must be the percentage, in user units, of the height parameter of the ‘viewBox’ applied to that viewport. If no ‘viewBox’ is specified, then the value to use must be the percentage, in user units, of the height of the SVG viewport.
UPDATE: I grouped all elements in the svg and supplied transform="scale(0.5 0.5)" and the percentage coordinate worked as expected. I start suspecting that the viewBox only transforms coordinates specified in user units and not in explicit units. But I would think the percentage should not be considered as an explicit unit as it is not really physical value. And it would contradict the spec excerpt from above. So what is it ?
function myFunction(){
document.getElementById("maxi").setAttribute("viewBox","0,0,492,124");
}
<svg id="maxi" version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="246" height="62" font-size="23px" xml:space="preserve" >
<line id="greenline" x1="0" y1="31" x2="232" y2="31" stroke="#00FF00" stroke-width="4"/>
<line id="blueline" x1="0" y1="50%" x2="232" y2="50%" stroke="#0000FF"/>
<path class="cutContour" fill="none" stroke="#EC008C" stroke-miterlimit="3.8637" d="M6.8,2.3H225
c2.3,0,4.3,1.9,4.3,4.3v48.2c0,2.3-1.9,4.3-4.3,4.3H6.8c-2.3,0-4.3-1.9-4.3-4.3V6.6C2.5,4.2,4.4,2.3,6.8,2.3z"/>
</svg>
<input type="button" value="Click Me" onClick="myFunction();">
I'm not seeing anything unusual about the behaviour you're describing. See Codepen at https://codepen.io/MSCAU/pen/JapPQd.
The blue line is staying in the (vertical) centre of the SVG as its Y-coordinates are expressed as 50%. When the viewBox changes value, this is not affected. Only its X values are expressed in absolute terms so the line gets halved when the viewBox is made explicit.
The green line is getting displaced in X- and Y-axes when the button is clicked as the coordinate system is changing - in your example doubling - and its coordinates are expressed in absolute terms.
I changed the colours and stroke-widths to make it clearer what's going on. I've also commented out your PATH and put a (dotted) border round the SVG for clarity. I have also added a handy reset button:
function reset() {
document.getElementById("maxi").removeAttribute("viewBox");
}

Mixing stroke width units in SVG

This seemed to be working before I added a viewBox (which was required since paths are in user units).
<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="7in" height="7in" viewBox="0 0 7 7">
<rect x="2" y="2" width="1" height="1" style="fill:none;stroke-width:1px;stroke:rgb(0,0,0)" />
</svg>
Now, if I look in inkscape, the stroke-width is 90px (1in). Also, if I specify the rect size or position in inches, it gets a lot bigger (or moves to a bigger coordinate) than I would think it should. I know I'm missing something but reading through the viewbox and viewport docs are not leading me anywhere enlightening (they mostly discuss in terms of pixels). Could someone steer me in the right direction here?
Without the viewBox you have a viewport of 7in square. In CSS 1in is always 96px so your viewport would be 96 pixels across. If you drew a rect 1in across it would therefore occupy 96 pixels of the screen. (Inkscape may do something different but all browsers use 96px = 1in).
When you add a viewBox you add scaling into the mix. 1px on your drawing may no longer represent 1px on the screen. I.e. 1in on your drawing is still 96px on your drawing but no longer 96 pixels on the screen.
Your current viewBox says that 7px on the screen is now 7in or 7 x 96px so everything on the drawing is magnified by a factor of 96. 1in on the drawing is now 96 x 96 pixels on the screen.

SVG viewBox breaks 100% fill of viewPort while preserving aspect ratio

Q: How can I use the viewBox coordinate system whilst still filling the viewPort completely and preserving aspect ratio?
I'm new to svg programming, so hopefully I'm just mis-understanding a basic concept.
I want to create an interactive & responsive map with , based on a background image that the user uploads.
Here's the basic example I'm trying to get to work (JSFiddle):
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200px"
height="400px"
preserveAspectRatio="xMidYMid meet"
style="border: 1px solid black;">
<image x="0" y="0" width="100%" height="100%"
preserveAspectRatio="xMidYMid meet"
xlink:href="http://www.bized.co.uk/sites/bized/modules/bized_cb_navigation/images/floorplan_info.gif">
</image>
</svg>
This works nicely. since however the viewPort changes, the image always fills it whilst maintaining its aspect ratio. (See wide-Screen example)
Next I add a coordinate system viewBox="0 0 100 100":
the wide-screen view still fills nicely
but the vertical-screen view now does not fill the viewPort anymore
If you take a different image that is wider than tall, then the wide-screen view breaks, and the vertical-screen view still works.
When I inspect the SVG in Chrome DOM Element inspector, for the first two examples without using viewBox="0 0 100 100" The svg element has the same size as the viewPort. Once the viewBox attribute is added, the element becomes a square with sides equal to the lesser of the viewPort's sides.
This behavior is explained in this Tutorial as:
"... the view box is scaled according to the smaller of the two aspect ratios..."
I need the viewBox attribute so that I can zoom and pan on the image within the viewPort.
This is because you effectively have two competing viewBox transformations.
Because of your square viewBox, you are fitting the image into a square, and then fitting the square into your SVG rectangle.
If you make your SVG viewBox the same dimensions as your image (or the same aspect ratio will do), then the problem will be resolved.
viewBox="0 0 155 210"
http://jsfiddle.net/2qexypLs/15/
http://jsfiddle.net/2qexypLs/16/
My current solution is to use JavaScript to dynamically set the viewBox width and height values to the same value as svg width and height values. That way the aspect ratio for x and y are the same and the fill returns to 100% of viewPort. (JsFiddle)
For the interactive elements layer of the map I have a separate coordinate system that is mapped to the background image scaling ratio when the svg viewPort is defined. That means all coordinates need to be recalculated on svg define/change width/height event.
After this first re-calculation, the map can be zoomed and panned by changing the viewBox parameters without any further calculations.

Constant border in a dynamic SVG graphic

I want to have a rectangle that takes all the place in a SVG file. It should also have a border (3px stroke width). The size of the graphic should be easy changeable (by changing attributes "width" and "height" of the "svg" node). I came up with following construction:
<svg width="150" height="35" >
<g>
<rect
id="rect6648"
style="fill:#ffffff; fill-opacity:1; stroke:#000000; stroke-width:3;"
x="0"
y="0"
width="100%"
height="100%" />
</g>
</svg>
But it produces following image with dirty border:
I need something like this:
Is it possible at all? As mentioned before it must work for any size of the graphic.
Thanks in advance!
Alas, no, at least not with purely declarative SVG. The stroke on a shape is painted on both sides of the geometric line that defines that shape (in your case, there's 1.5 on either side). Because of that, it will get clipped for a shape that fills the whole viewbox.
In which context are you using this? You should be able to script it: get the size of the viewbow, on rect set x and y to stroke-width/2, width to width - stroke-width and height to height - stroke-width. If in a dynamic context you will need to detect resizes, but that's often possible.
You need to place the ractangle at half pixel coordinates like x="0.5" y="0.5", then the borders won't be blurry. Also add vector-effect:non-scaling-stroke to the rectangle's CSS to be sure that the border is always 3px wide regardless of zoom level.

Resources