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

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");
}

Related

How do I get svg vmin right?

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>

how does fill-rule="evenodd" work on a star SVG

I saw the following svg shape when i was trying to understand fill-rule in SVG
<div class="contain-demo">
<svg width="250px" height="250px" viewBox="0 0 250 250">
<desc>Yellow star with intersecting paths to demonstrate evenodd value.</desc>
<polygon fill="#F9F38C" fill-rule="evenodd" stroke="#E5D50C" stroke-width="5" stroke-linejoin="round" points="47.773,241.534 123.868,8.466 200.427,241.534 7.784,98.208 242.216,98.208 " />
</svg>
</div>
Please note the following:
The SVG has just one path.
The SVG has intersecting points.
if i change fill-rule="nonzero" the entire SVG get the fill.
Currently with fill-rule="evenodd" applied the SVG's central area does't get the fill.
Why is it that with fill-rule="evenodd" the central portion of the star SVG is not filled ?
I did read the spec for fill-rule="evenodd"
This value determines the "insideness" of a point in the shape by
drawing a ray from that point to infinity in any direction and
counting the number of path segments from the given shape that the ray
crosses. If this number is odd, the point is inside; if even, the
point is outside.
But i still don't understand why when i apply fill-rule="evenodd", the middle of the star is not filled. Can somebody please explain this ?
This is how the mechanism works. Pick a point in the center, draw lines to infinity in any direction - they always cross an even number of path segments -which means that they are "outside" and their areas don't get filled.
Pick a point in the filled triangles - if you draw lines to infinity in any direction - they always cross an odd number of path segments and thus, they are "inside" and their area should be filled.

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.

How can I have a cumulative translate within an SVG patternTransform?

Consider the following SVG:
<?xml version="1.0" standalone="no"?>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern0" patternTransform="skewX(5)" x="0" y="0" width="0.1" height="0.042">
<line x1="0" x2="1200" y1="0" y2="0" stroke-dasharray="25,25" style="stroke:#000000;stroke-width:1" />
</pattern>
</defs>
<rect fill="url(#Pattern0)" width="600" height="600" style="stroke:#0000FF;stroke-width:1" />
</svg>
Here's the fiddle to see it in action.
I'm trying to stagger the dashed lines in this pattern by some offset. The example above is a horizontal line, but the real lines may be rotated to any angle. The offset should be defined in the same units as the dasharray.
Adding a patternTransform="translate(x)" to the pattern doesn't work -- it just offsets the position of the entire repeated pattern instead of translating each repeated line in a cumulative fashion. Same goes for adding transform="translate(x)" to the line.
Adding a patternTransform="skewX(x)" works for horizontal lines and patternTransform="skewY(x)" works for vertical ones, but it doesn't work right at all for lines at other angles. Also, skewX/skewY require an angle as the argument, and I really need to move the lines based on the same units as the dashArray.
EDIT: Without boring anyone with the gory details, my ultimate goal is to translate AutoCAD hatch patterns into SVG files that I can use as background images in HTML. To see a little about how these patterns work, please see some of the examples here:
AutoCAD 2010 User Documentation (Note the three links on the left -- overview, dashed lines, and multiple lines).
I would love to use an SVG pattern to do this, but it's looking more and more like I'll have to return to previous attempts of using a loop to create individual lines. I was trying to avoid this because frankly I've forgotten way too much high school trig and getting enough lines (at an arbitrary angles) to fill the canvas is eluding me.
There isn't a short-cut way to create a staggered pattern. You'll need to draw out a set of staggered lines yourself, up to the point where you can get the effect you want with a repeating (possibly skewed or rotated) rectangular grid.
To get the effect of two dashed lines, with the dashes offset to each other, your repeating tile needs to have two lines.
<?xml version="1.0" standalone="no"?>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern0" patternUnits="userSpaceOnUse"
x="0" y="0" width="100%" height="8.4%"
style="stroke:#000000;stroke-width:1;
stroke-dasharray:25,25; " >
<line x1="0" x2="100%" y1="1%" y2="1%" />
<line x1="0" x2="100%" y1="5.2%" y2="5.2%"
style="stroke-dashoffset:25" />
</pattern>
</defs>
<rect fill="url(#Pattern0)" width="600" height="600"
style="stroke:#0000FF;stroke-width:1" />
</svg>
Explanation of the changes:
I've changed to userSpaceOnUse units to define the pattern tile. This ensures that percentages defined when drawing the pattern are interpretted the same way as percentages drawn when defining the size of the tile. (User space is default for patternContentUnits.) The rectangle in your sample was the same size as the user space, so it wasn't making a difference, but it does if you change the size of the rectangle.
If you really wanted the spacing of the lines to be proportional to the size of the rectangle being filled, this is a problem. You could change both patternUnits and patternContentUnits to objectBoundingBox (the default for patternUnits), but then you have to define all your stroke properties relative to the rectangle size as well.
I've doubled the height of the tile, so that it is equivalent to two of your original tiles. One tile now includes two horizontal lines.
I've offset the first line slightly so it doesn't get cut off by the edge of the tile (you were only getting a half-pixel stroke width displayed, and the difference becomes obvious if the second line isn't similarly clipped).
I've added the second line, with the dash pattern offset, at a vertical position that is half the tile height below the first line (1% + 8.4%/2 = 5.2%).
Like #Robert Longson, I'm not 100% certain I understand what you're asking, so if this isn't the effect you want please post a drawing of what you're trying to achieve.

Resources