SVG Scaling and viewBox - svg

Here is a simple SVG file:
<svg xmlns="http://www.w3.org/2000/svg" width="100mm" height="100mm" version="1.1" viewBox="0 0 377.95 377.95">
<rect x="0" width="189" height="189" stroke="black" stroke-width="6" fill="red"/>
</svg>
This renders a 100mm x 100mm box with the top left corner at the origin as expected.
If I change the viewbox to:
viewBox="0 0 177.95 177.95"
then the box is scaled up, still with the top left corner at the origin, as expected.
However, if I change only the width of the view box like so:
viewBox="0 0 177.95 377.95"
then then box is not scaled but is moved along the X axis.
I thought only the first two parameters of the viewbox affected the translation? Also why isn't the box stretched in the X direction?
Does the viewbox scaling only work correctly if the scaling is the same in both X and Y directions?
Thanks!

How the viewport is scaled in actual dimensions (embed frame) of the SVG is governed by preserveAspectRatio attribute.
See MDN entry and/or refer too Understanding SVG Coordinate Systems and Transformations by Sara Soueidan.

Related

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.

SVG clipPaths clipping objects more than expected. What could be the reason?

I've recently found out that the clipPaths in SVG clip a bigger area than what's specified in the commands.
Below is my code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<rect id="r1" x="100.85" y="100.39" width="200.51" height="100.72" fill="black"/>
<rect id="r2" x="100.85" y="100.39" width="200.51" height="100.72" fill="yellow"/>
<clipPath id="clip">
<use xlink:href="#r2"/>
</clipPath>
</defs>
<use xlink:href="#r1"/>
<use xlink:href="#r2" clip-path="url(#clip)"/>
</svg>
According to the code, the rect "r1" should be completely overlapped by the clipped rect "r2" and only r2 should be seen. But that's not the case. There is a black border seen on the output.
What could be the reason? As all browsers (Chrome, Firefox, Safari) seem to show the exact behavior, I was wondering if I made a mistake when understanding the specification.
I have also tried applying shape-rendering="crispEdges" attribute, wondering that Smoothing could be the culprit, but it made no difference in this case.
Further, I have found out that if the values are all integers (or maps to pixels exactly), this border goes away.
Would appreciate any input regarding this problem.
Thanks in advance.
The clip is working fine. What you are seeing is the effects of anti-aliasing.
Because your rectangles are not perfectly aligned with the pixels of the screen, some border pixels of the rectangles are being drawn with less than 100% opacity.
So when the black rectangle is drawn, it has some border pixels that are draw as a mix of black and the background colour (white).
+-----+-----+- -
|grey |grey |
+-----+-----+ ..etc..
|black|black|
+-----+-----+- -
Then the yellow rectangle is being drawn exactly on top of it. This time, the rectangle border pixels are being drawn as a mix of yellow and those black-white (ie. grey) pixels drawn earlier underneath.
+-----------+-----------+- -
|yellow/grey|yellow/grey|
+-----------+-----------+ ..etc..
| yellow | yellow |
+-----------+-----------+- -
If you draw your rectangles at exact pixel boundaries, that anti-alias bleed won't be visible.
Demo here

How to set SVG polygon to be fully stretched on any screen resolution?

i need to create polygon which will be responsive to any screen resolution and it will be always fully stretched to screen.
I started with this demo but as you can see it is woriking only for fixed width (1000px) of screen.
Is it possible to set polygon's coordinates with percentage units or is there any other way how to accomplish that?
You need to set a viewBox rather than a fixed width and height.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 20" preserveAspectRatio="none" width="100%" height="100%">
<polygon points="0,0 1000,0 500,20"
style="fill:#cc3333;"/>
</svg>

Better way to position svg:polygon

The SVG elements <line>, <circle>, <rect>, <text> and , <image> allow for positioning by x and y based off of the view port. Furthermore they can also be relatively positioned. Is there any way to accomplish this for <polygon> than to wrap it in an <svg>? The closest substitute for <polygon>, <path>, also has this... issue.
Based on the excellent comment by #Michael Mullany I was able to find a solution to the issue. By putting the polygon or path in side a <defs> tag it can be used later on in a <use> tag. The <use> tag allows for setting of x and y attributes that function the same as the attributes of other simple shapes like <line>, <circle>, <rect>, <text>
http://jsbin.com/iqEkAsE/2
<svg width="100%" height="100%">
<defs >
<path id="Triangle"
d="M 1 1 L 200 1 L 100 200 z"
fill="orange"
stroke="black"
stroke-width="3" />
</defs>
<use x="33%" y="33%" xlink:href="#Triangle"/>
<use transform="scale(-1)" x="-66%" y="-66%" xlink:href="#Triangle"/>
</svg>
It would be nice to be able to scale the shape dynamically by setting the width and height property of the <use> to a percent but it can still be scaled with a transform.
Use transformations (translation, rotation, scale), that is the correct way to do it.
The other shapes allow you to use a point defined by x,y coordinates because that's just a part of the shape definition (i.e. how do you define a circle, you need the center point and the radius).
In theory you could position an element using its bounding box, however the bounding box is not "settable" (there is no setBBox method), there is only getBBox()

Resources