Understanding svg's viewbox attribute - css

I am trying to use Turkey's map which was created before here.
I have changed the dimensions of svg element
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
width="800"
height="600"
style="shape-rendering:geometricPrecision;
text-rendering:geometricPrecision;
image-rendering:optimizeQuality;
fill-rule:evenodd;
clip-rule:evenodd"
viewBox="0 0 180 180">
When I change viewBox values to
viewBox="0 0 200 200"
the map is rendered in smaller size.
If I set the values to "0 0 10 10" image is not displayed.
If I set the values to "0 0 50 50" a huge image comes overflowing the page
If I set the values to "0 0 100 100" image is fine.
But I don't understand how it is working.

An SVG canvas is basically an endless plain, where you can put all your objects in.
The width and height you define the size of the image as shown in the browser. This works pretty much like with any other image you can include in HTML.
With the viewBox attribute on the other hand you select the part of that plain, you currently want to view.
So with a value of "0 0 10 10" you set the upper left point of the shown image to the point 0/0 and from there select 10 units to the right and bottom for your image. In your example in those upper 10 times 10 units area there is nothing, hence you see just a transparent area, which you perceive as "not being displayed".
With a value of "0 0 50 50" the shown area gets bigger and you'll start to see the upper left corner of the picture. The first parts of the map become visible.
Finally with "0 0 100 100" you can see pretty much half of the upper map.
The area you select using viewBox is the scaled to the height and width of the SVG element. With both combined you can enabled stuff like zooming into an SVG.
You can control the scaling with the attribute preserveAspectRatio.
You can add the following code at the bottom of the SVG file and see the displayed boxes (make sure to set the viewBox="0 0 180 180" before):
<rect x="0" y="0" width="100" height="100" style="stroke: blue; stroke-width: 1; fill: white; opacity: 0.8" />
<rect x="0" y="0" width="50" height="50" style="stroke: green; stroke-width: 1; fill: none;" />
<rect x="0" y="0" width="10" height="10" style="stroke: red; stroke-width: 1; fill: none;" />
<text x="5" y="97" style="fill: blue; font-size: 5px;">100x100</text>
<text x="5" y="47" style="fill: green; font-size: 5px;">50x50</text>
<text x="5" y="15" style="fill: red; font-size: 5px;">10x10</text>
So to conclude the purpose of viewBox is to select that part of the endless SVG plane, that should actually be rendered. If you choose the wrong values here, you might just see an empty space.
Links:
preserveAspectRatio #MDN
viewBox #MDN

Viewbox with the width and height values are how you can do zooming, and panning with SVG.
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
width="800"
height="600"
viewBox="0 0 800 600">
no scaling 800 pixels width in the viewbox equals 800 pixel width on the screen
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
width="800"
height="600"
viewBox="0 0 1600 1200">
Everything is scaled down so that a line 1600 pixels wide only shows up as 800 pixels wide.
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve"
width="800"
height="600"
viewBox="400 300 400 300">
Zoom into the bottom right hand 1/4 of the 800 by 600 screen making that portion fill the entire svg, don't show anything above the 300 pixel line or to the left of the 400 pixel line.

Related

How to scale svg to viewport, but keep aspect ratio on filled image

I want to fill a viewport with an svg, and add a fill to a path in this svg.
When I set preserveAspectRatio="xMinYMin slice" to the image pattern, it will preserve it's aspect ratio nicely, but the path will not scale.
If I then add preserveAspectRatio="none" to the svg element, the whole element will fit to the viewport, but my image will not scale properly.
What am I overlooking?
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 750" preserveAspectRatio="none">
<defs>
<pattern id='home' width="1" height="1" preserveAspectRatio="xMinYMin slice">
<image xlink:href='https://i.imgur.com/4AiXzf8.jpg' width="100%" height="100%" preserveAspectRatio="xMinYMin slice"></image>
</pattern>
</defs>
<path id="bg" class="cls-1" d="M0,0V622.58S250,711,683,711s683-88.42,683-88.42V0Z"style="fill: url(#home)"></path>
</svg>
If you set the whole SVG to preserveAspectRatio="none", then everything inside the SVG will stretch and there is no way to counteract that. So you have to do it a different way.
What we have to do is remove the viewBox and the preserveAspectRatio and just set the width of the SVG to 100% and the height to the height of our <path> (711px). That way the SVG viewport fills the width of the page and keeps it's height at the size we want.
The next step is to move the <image> out of the <pattern>, make its width and height 100% x 100% and set preserveAspectRatio="xMinYMin slice". So it now scales to fill our wide SVG, and keeps its aspect ratio.
The last step is to apply a <clipPath> to the image to give it the shape we want. To get the clip path to automatically fit itself to the <image>, we need to use clipPathUnits="objectBoundingBox".
The thing with objetBoundingBox units is that (0,0) represents the top left of the element it is applied to, and (1,1) corresponds to the bottom-right of the element it is applied to. But our path is much bigger than that. It has a width and height of 1366x711.
To fix that, we need to scale the path so that it is only 1x1 in size, instead of 1366x711. So we apply a transform and scale it by 1/1366 in X, and 1/711 in Y.
transform="transform="scale(0.000732, 0.001406)"
The final result is this:
body {
margin: 0;
padding: 0;
}
<svg width="100%" height="711px" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<path d="M0,0V622.58S250,711,683,711s683-88.42,683-88.42V0Z"
transform="scale(0.000732, 0.001406)"></path>
</clipPath>
</defs>
<image xlink:href='https://i.imgur.com/4AiXzf8.jpg' width="100%" height="100%" preserveAspectRatio="xMinYMin slice"
clip-path="url(#clip)"></image>
</svg>
remove height and preserveAspectRatio but keep viewBox as it is and width at 100%.

Scaling SVG image but not the mask

I am trying to render a image where the bottom part is cut of stylishly. The image should scale and fill the container so it is always 100% wide, but remains 500px high.
The current state almost works, however I do not want the mask to scale on the y-axis as it does now. The mask should stay a fixed height and only scale on the x-axis. The effect right now is that the mask cuts of a way to big piece of the image in large formats.
<svg viewBox="0 0 100 50" preserveAspectRatio="xMinYMax slice" width="100%" height="500px">
<defs>
<mask id="clip">
<path d="M0,45 100,40 100,0 0,0Z" fill="white"></path>
</mask>
</defs>
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://placeimg.com/1500/500/animals" mask="url(#clip)" width="100%" height="100%" x="0" y="0" preserveAspectRatio="xMidYMid slice"></image></svg>
You are not very explicit about how you want the mask to behave when the image gets wider.
Here's one solution. Is this what you wanted? The mask angles from full height on the left, to 90% height on the right. No matter how wide the image is.
<svg width="100%" height="500px">
<defs>
<mask id="clip" maskContentUnits="objectBoundingBox">
<path d="M0,1 1,0.9 1,0 0,0Z" fill="white"></path>
</mask>
</defs>
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://placeimg.com/1500/500/animals"
width="100%" height="100%" x="0" y="0" preserveAspectRatio="xMidYMid slice" mask="url(#clip)"></image>
</svg>

Using SVG <image> as cover inside proportion less SVG

DEMO
Objective: I am trying to create a triangle shaped proportion less image in HTML.
My Approach: I have decided to use SVG to achieve this by creating a polygon triangle that can stretch & shrink to fit any dimension and used it as a mask on image that is suppose to fit in any dimension without loosing its own proportion.
Issue: Although the shape is working as I want but the background image stretches with the shape, is there any way I can make the image behave to something similar like css background-size: cover property.
Code:
HTML
<div id="svg-container">
<svg width='100%' height='100%' viewBox="0 0 100 100" preserveAspectRatio="none" style='background-color: whitesmoke'>
<defs>
<polygon id="mask" points="0,0 0,100 0,100 100,0" />
<pattern id="image" patternUnits="userSpaceOnUse" width="100" height="100" x="0" y="0">
<image xlink:href="http://lorempixel.com/500/500" x="0" y="0" width="100%" height="100%" preserveAspectRatio="xMinYMin slice"/>
</pattern>
</defs>
<use xlink:href="#mask" fill="url(#image)"></use>
</svg>
</div>
CSS:
#svg-container {
width: 100%;
height: 100px;
}
To check same issue using SVG image tag here.

SVG Circle Scaling

I have a simple SVG circle:
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="100px" height="100px" viewBox="0 0 100 100" overflow="visible" enable-background="new 0 0 100 100"
xml:space="preserve">
<circle fill="#6E6F6F" cx="50" cy="50" r="49"/>
</svg>
This image is being used as a background, and resized to 22px:
background: transparent url('++resource++svg/star_neg.svg') no-repeat 0 0 / 22px 22px;
When I view this in the browser, the right and bottom sides of the circle appear flat in Firefox (Chrome looks fine). If I zoom in on Firefox, the circle appears complete as expected. How can I fix this?
Finally found a simple solution. The div displaying the svg as the background should be larger than the SVG. I had the box at 22px square, the same size I was setting the SVG. To fix the issue, I displayed the box as 25px, with the background image centered instead of set in a corner.

Using CSS approach how to set an image to fill a path in SVG?

I want to create a CSS class to fill a path with image that can be applied on any SVG path and fill that path with image. The image must be stretch to fit that path.
To achieve this; I create a pattern with image tag and set the width and height as 100%. but the image takes 100% of the whole screen instead of objectBoundingBox of the container (in this case svg tag).
Below is the sample code:
.myClass {
fill: url(#image);
stroke: red;
stroke-width: 5;
}
<svg id='pattern' xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id='image' x=0 y=0 width="100%" height="100%" patternUnits='objectBoundingBox'>
<image xlink:href='myImage.png' x=0 y=0 width="100%" height="100%" preserveAspectRatio="none"></image>
</pattern>
</defs>
</svg>
<svg id='triangle' xmlns="http://www.w3.org/2000/svg" version="1.1" width='300px' height='300px'>
<path d='M0 0 L300 0 L300 300 Z' class='myClass'></path>
</svg>
May be I am doing something wrong.
Please suggest any solution for this problem.
Here's your thing working - http://jsfiddle.net/eAfTc/
.myClass {
fill: url(#image);
stroke: red;
stroke-width: 5;
}
<svg id='pattern' xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id='image' width="1" height="1" viewBox="0 0 100 100" preserveAspectRatio="none">
<image xlink:href='http://dummyimage.com/600x400/abc/333' width="100" height="100" preserveAspectRatio="none"></image>
</pattern>
</defs>
</svg>
<svg id='triangle' xmlns="http://www.w3.org/2000/svg" version="1.1" width='300px' height='300px'>
<path d='M0 0 L300 0 L300 300 Z' class='myClass'></path>
</svg>
Note that there's a patternContentUnits and a patternUnits, they do different things. Personally I prefer to use a viewBox for defining the coordinate system.
Here's a new example showing the pattern applied to various elements of different sizes and aspect ratios, it also gets rid of the first svg fragment.
Update: I added 'preserveAspectRatio' to the <pattern> element, and a new example showing the stretching and scaling.

Resources