Responsive SVG sizing - svg

I have the following SVG:
body {
background-color: #dad9c7;
svg {
position: absolute;
width: 100%;
height: 400px;
margin: 0 auto;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.
<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg" version="1.1">
<g>
<rect width="1000" height="151" x="0" y="0" fill="#d5835b" />
<rect width="1000" height="151" x="0" y="150" fill="#d47966" />
<rect width="1000" height="126" x="0" y="300" fill="#b66961" />
<rect width="1000" height="101" x="0" y="425" fill="#d17385" />
<rect width="1000" height="101" x="0" y="525" fill="#aa617c" />
<rect width="1000" height="101" x="0" y="625" fill="#a36d8f" />
<rect width="1000" height="101" x="0" y="725" fill="#736d87" />
<rect width="1000" height="176" x="0" y="825" fill="#313d53" />
</g>
</svg>
Which looks like this:
How can I do the following?
Keep heights of colored strips the same when scaling the window (not scale).
Stretch colored strips horizontally to the edges of the viewport on either side left and right.
Stretch the top-most rectangle to the top of the screen so the upper third of the viewport is orange and stretch the bottom-most rectangle to the bottom of the viewport so that the lower third of the viewport is purple.
Always keep the "square" centered vertically which already works with CSS, but however the SVG is manipulated to solve would have to keep this into account.
Here is an example of how this would look: As the window gets taller, the colored rectangles will stay in the middle, but the top orange and bottom purple would be cut off based on the height of the viewport.

How can I do the following?
Keep heights of colored strips the same when scaling the window (not scale).
You are already doing this by setting height to 400px.
Stretch colored strips horizontally to the edges of the viewport on either side left and right.
Set preserveAspectRatio="none" on the SVG. See below.
body {
background-color: #dad9c7;
}
svg {
position: absolute;
width: 100%;
height: 400px;
margin: 0 auto;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<svg viewBox="0 0 1000 1000" preserveAspectRatio="none">
<g>
<rect width="1000" height="151" x="0" y="0" fill="#d5835b" />
<rect width="1000" height="151" x="0" y="150" fill="#d47966" />
<rect width="1000" height="126" x="0" y="300" fill="#b66961" />
<rect width="1000" height="101" x="0" y="425" fill="#d17385" />
<rect width="1000" height="101" x="0" y="525" fill="#aa617c" />
<rect width="1000" height="101" x="0" y="625" fill="#a36d8f" />
<rect width="1000" height="101" x="0" y="725" fill="#736d87" />
<rect width="1000" height="176" x="0" y="825" fill="#313d53" />
</g>
</svg>
Stretch the top-most rectangle to the top of the screen so the upper third of the viewport is orange and stretch the bottom-most rectangle to the bottom of the viewport so that the lower third of the viewport is purple.
You cannot automatically stretch the rectangle itself with CSS. But one way you could do it is to use pseudo elements to colour the top and bottom halves of the parent element with matching colours.
body {
background-color: #dad9c7;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 0;
margin: 0;
}
svg {
position: absolute;
width: 100%;
height: 400px;
margin: 0 auto;
display: block;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
body::before {
content: "";
display: block;
position: absolute;
width: 100%;
top: 0;
bottom: 50%;
background-color: #d5835b;
}
body::after {
content: "";
display: block;
position: absolute;
width: 100%;
top: 50%;
bottom: 0;
background-color: #313d53;
z-index: -1;
}
<svg viewBox="0 0 1000 1000" preserveAspectRatio="none">
<g>
<rect width="1000" height="151" x="0" y="0" fill="#d5835b" />
<rect width="1000" height="151" x="0" y="150" fill="#d47966" />
<rect width="1000" height="126" x="0" y="300" fill="#b66961" />
<rect width="1000" height="101" x="0" y="425" fill="#d17385" />
<rect width="1000" height="101" x="0" y="525" fill="#aa617c" />
<rect width="1000" height="101" x="0" y="625" fill="#a36d8f" />
<rect width="1000" height="101" x="0" y="725" fill="#736d87" />
<rect width="1000" height="176" x="0" y="825" fill="#313d53" />
</g>
</svg>
Always keep the "square" centered vertically which already works with CSS, but however the SVG is manipulated to solve would have to keep this into account.
N/A here.
Alternate pure-SVG solution
There is also a pure SVG solution using nested <svg> elements. The only CSS we are using is just to ensure the SVG occupies the full size of the page.
It works by making the top and bottom rectangles extend outside the viewBox by an extra 1000 pixels. To make sure they are visible, we set overflow="visible". 1000 is an arbitrary value. If you want to support screens > 2400 pixels high, then you could choose a larger value.
The inner SVG gets centred vertically using a combination of a y offset and a transform that shifts it up by 200px. This is equivalent to the common top: 50%; transform: translate(0,-50%)" trick to vertically centre CSS block elements.
body {
background-color: #dad9c7;
padding: 0;
margin: 0;
}
#mysvg {
position: absolute;
display: block;
width: 100%;
height: 100%;
}
<svg id="mysvg">
<g transform="translate(0, -200)">
<svg width="100%" height="400px"
y="50%" transform="translate(0, -200)"
viewBox="0 0 1000 1000" preserveAspectRatio="none"
overflow="visible">
<g>
<rect width="1000" height="1151" x="0" y="-1000" fill="#d5835b" />
<rect width="1000" height="151" x="0" y="150" fill="#d47966" />
<rect width="1000" height="126" x="0" y="300" fill="#b66961" />
<rect width="1000" height="101" x="0" y="425" fill="#d17385" />
<rect width="1000" height="101" x="0" y="525" fill="#aa617c" />
<rect width="1000" height="101" x="0" y="625" fill="#a36d8f" />
<rect width="1000" height="101" x="0" y="725" fill="#736d87" />
<rect width="1000" height="1176" x="0" y="825" fill="#313d53" />
</g>
</svg>
</g>
</svg>

You cannot use media queries or css styling for that since a svg does not support that. If you really need to that with an SVG, you will need some Javascript to accomplish your desired effect. In your case, I guess it is simpler to create that using html and css with some media queries.
The only thing you can control when scaling/displaying a SVG is the preserveAspectRatio attribute. A detailed description can be found here.

Related

Reusing SVG elements while inserting different text?

I want to display balls with 1,2,3,4 inside. Can I use <use>? or I must duplicate the stone <g>?
#stone text {
fill: grey;
dominant-baseline: middle;
text-anchor: middle;
font-size: 0.33pt;
}
#pane {
background-color: yellow;
width: 100%;
height: 100%;
}
.stone-white {
fill: #F2F4F4;
}
.stone-black {
fill: #273746;
}
<svg id="pane" viewBox="0 0 22 22" preserveAspectRatio="none">
<defs>
<g id="stone">
<circle cx="0" cy="0" r="0.45" />
<text>333</text>
</g>
</defs>
<use href="#stone" class="stone-white" x="1" y="1"/>
<use href="#stone" class="stone-white" x="2" y="2"/>
<use href="#stone" class="stone-black" x="3" y="2"/>
<use href="#stone" class="stone-black" x="2" y="4"/>
</svg>

Mask Image with SVG Shape and add a Border

Not sure this is possible, so I have a back-up plan (use a background image and :after to mask one of 3x transarent PNGs with the border in the image and use nth-child to change them).
Here's what I'm trying to accomplish. I know clip-path will give me the image mask, but I need the curved edges and the border color, too. Which I don't think is possible in pure CSS.
<!DOCTYPE html>
<html>
<style>
img {
max-width: 100%;
display: block;
}
.container {
width: 300px;
height: 300px;
margin: 1em auto;
}
.frame-border {
stroke: #10c020;
stroke-width: 4;
}
</style>
<body>
<div class="container">
<svg viewBox="-10 -10 120 120">
<defs>
<mask id="mask">
<rect fill="#000000" x="0" y="0" width="300" height="300"></rect>
<path id="Path_611" data-name="Path 611" d="M1,38a12.225,12.225,0,0,1,2.558-3.025L41.351,13.462A21.12,21.12,0,0,1,46.733,12.4a14.319,14.319,0,0,1,4.81.765L89.2,34.814A7.333,7.333,0,0,1,92,37a7.273,7.273,0,0,1,1,3.4v45.3A6.741,6.741,0,0,1,92,89a12.9,12.9,0,0,1-3.015,2.945L50.42,110.628a8.953,8.953,0,0,1-3.688.786,13.383,13.383,0,0,1-4.153-.992L4.2,92.012A12.105,12.105,0,0,1,1,89a7.112,7.112,0,0,1-1-3.581V41.534A9.569,9.569,0,0,1,1,38Z" transform="translate(1.502 -10.892)" fill="#FFFFFF"/>
</mask>
</defs>
<image mask="url(#mask)"
xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://randomuser.me/api/portraits/women/47.jpg" width="100" height="100">
</image>
<g class="frame-border" fill="none">
<path id="Path_611" data-name="Path 611" d="M1,38a12.225,12.225,0,0,1,2.558-3.025L41.351,13.462A21.12,21.12,0,0,1,46.733,12.4a14.319,14.319,0,0,1,4.81.765L89.2,34.814A7.333,7.333,0,0,1,92,37a7.273,7.273,0,0,1,1,3.4v45.3A6.741,6.741,0,0,1,92,89a12.9,12.9,0,0,1-3.015,2.945L50.42,110.628a8.953,8.953,0,0,1-3.688.786,13.383,13.383,0,0,1-4.153-.992L4.2,92.012A12.105,12.105,0,0,1,1,89a7.112,7.112,0,0,1-1-3.581V41.534A9.569,9.569,0,0,1,1,38Z" transform="translate(1.502 -10.892)" stroke-linecap="round" />
</g>
</svg>
</div>
</body>
</html>

SVG Fixed Sized Element Positioned Relatively within Viewbox

In the example SVG below, how can the marker/pin (red circle) be given a fixed size (say in pixels) whilst at the same time being relatively positioned within its parent viewbox? That is, so that the marker/pin is always visually the same size but can easily be positioned relative part of the map we are viewing.
We've added the code to a fiddle here: https://jsfiddle.net/krbgxqtm/13/
Additional comments:
We would like to avoid the use of client-side code (i.e JavaScript).
We will not always know the container's dimensions exactly due to the responsive positioning it will be within.
We've seen examples of using CSS background-image for achieving the sizing, but this will not satisfy the size of the surrounding hyperlink.
Since your pin is a circle you can use a very short line stroke-linecap="round" vector-effect="non-scaling-stroke". The stroke-linecap="round" will give the line the aspect of a circle. The vector-effect="non-scaling-stroke" will keep the line unscaled.
Aditional observation:
If you intend to use a symbol the viewBox this is NOT correct: viewBox="0 0 10px 10px" Don't use px units. Also
svg {
background-color: khaki;
border: solid 1px #9c9c9c;
}
.container {
background-color: #9ecae1;
border: solid 1px #2c3e50;
margin-top: 20px;
width: 200px;
height: 150px;
}
.container-2 {
width: 400px;
height: 150px;
}
.container-3 {
width: 250px;
height: 300px;
}
.svg-container {
width: 100%;
height: 100%;
}
.outline {
fill: #9c9c9c;
}
<svg width="0" height="0">
<defs>
<path id="country-PT" class="country" stroke="#141414" stroke-opacity="1" stroke-width="1" stroke-linecap="miter" stroke-linejoin="miter" fill="#4F4F4F" fill-opacity="1" fill-rule="evenodd" d="M93 531L101 527L101 532L119 530L119 534L124 536L115 544L117 555L114 557L116 560L114 565L108 565L115 574L110 583L113 586L115 586L109 595L110 601L104 604L96 602L92 603L94 596L93 582L95 583L95 581L89 583L90 579L86 578L87 569L91 566L95 546z"></path>
<g id="pin" class="pin-container" >
<line x1="10" x2="10.1" y1="10" y2="10" stroke-width="10" stroke="red" stroke-linecap="round" vector-effect="non-scaling-stroke" transform="translate(-5,-5)" />
</g>
</defs>
</svg>
<div class="container">
<svg class="svg-container" viewBox="85 525 43 83" >
<!-- Country -->
<rect class="outline" x="85" y="525" width="43" height="83" />
<use href="#country-PT"/>
<!-- Marker -->
<a href="#goToA">
<use x="85" y="574" href="#pin"/>
</a>
</svg>
</div>
<div class="container container-2">
<svg class="svg-container" viewBox="85 540 21.5 41.5" preserveAspectRatio="xMidYMid meet">
<!-- Country -->
<rect class="outline" x="85" y="525" width="43" height="83" />
<use href="#country-PT"/>
<!-- Marker -->
<a href="#goToA">
<use x="85" y="574" href="#pin"/>
</a>
</svg>
</div>
<div class="container container-3">
<svg class="svg-container" viewBox="85 540 20 40" preserveAspectRatio="xMidYMid meet">
<!-- Country -->
<rect class="outline" x="85" y="525" width="43" height="83" />
<use href="#country-PT"/>
<!-- Marker -->
<a href="#goToA">
<use x="85" y="574" href="#pin"/>
</a>
</svg>
</div>

fixed width and height of svg objects

I want to keep my polygon circle and path width same width and height on all devices for example 50px height and 50px width, because they are streched on mobile devices, or if there is another solution.
i deleted some css classes and other svg objects so code can be seen as simple.
<svg preserveAspectRatio="none" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 734 414">
<g id="bg">
<rect class="cls-1" x="-0.01" y="0.96" width="733.99" height="92.47" />
<g id="bg_elements">
<path class="cls-2" d="M734,414H0V81.84s56,2.53,74.56,2.73c22,.23,64.67-3,86.71-2.86,24.83.18,109.22,6.38,136.17,7.46,45.2,1.8,105-5.57,149.73-7.27,39.45-1.51,118.14,3.66,157.5,3.66,32.42,0,129.33-5.29,129.33-5.29Z" />
<g id="elements">
<polygon id="triangle" class="cls-3" points="32.27 247.14 23.13 224.36 7.97 243.67 32.27 247.14" />
<circle id="ellipse" class="cls-4" cx="317.06" cy="160.33" r="12.53" />
<polygon id="triangle_s" class="cls-5" points="460.08 371.37 448.65 376.05 438.89 383.61 440.56 371.37 438.89 359.14 448.65 366.7 460.08 371.37" />
<path id="plus" class="cls-6" d="M651.11,227l-13.62,21.69m-4-
17.66,21.7,13.63" />
</g>
</g>
</g>
</svg>
If the svg has the viewBox and preserveAspectRatio all set, another place possibility is to use a container div and font-size something like:
<div style="height: 50px; width: 50px; fontSize: 50px">
<svg>...</svg>
</div>
Your requirements are not clear. But perhaps what you want, is for the SVG to cover the whole of the viewport whilst still staying unstretched/unsquashed. Ie. the equivalent of background-size: cover for HTML elements.
If that is the case, then what you want is:
preserveAspectRatio="xMidYMid slice"
This will size the SVG up or down so that it is the minimum size that still entirely covers the whole of the viewport. However you may end up with some parts of the SVG getting clipped off the edges.
svg {
width: 400px;
height: 300px;
}
path {
fill: red;
}
circle {
fill: blue;
}
polygon {
fill: yellow;
}
<svg preserveAspectRatio="xMidYMid slice" viewBox="0 0 734 414">
<g id="bg">
<rect class="cls-1" x="-0.01" y="0.96" width="733.99" height="92.47" />
<g id="bg_elements">
<path class="cls-2" d="M734,414H0V81.84s56,2.53,74.56,2.73c22,.23,64.67-3,86.71-2.86,24.83.18,109.22,6.38,136.17,7.46,45.2,1.8,105-5.57,149.73-7.27,39.45-1.51,118.14,3.66,157.5,3.66,32.42,0,129.33-5.29,129.33-5.29Z" />
<g id="elements">
<polygon id="triangle" class="cls-3" points="32.27 247.14 23.13 224.36 7.97 243.67 32.27 247.14" />
<circle id="ellipse" class="cls-4" cx="317.06" cy="160.33" r="12.53" />
<polygon id="triangle_s" class="cls-5" points="460.08 371.37 448.65 376.05 438.89 383.61 440.56 371.37 438.89 359.14 448.65 366.7 460.08 371.37" />
<path id="plus" class="cls-6" d="M651.11,227l-13.62,21.69m-4-
17.66,21.7,13.63" />
</g>
</g>
</g>
</svg>

Scrollable image in SVG

I am trying to replicate this HTML in an SVG:
<div>
<img src="tallimg.jpg">
</div>
div {
overflow: hidden;
width: 10%;
height: 50%;
}
img {
width: 100%;
height: auto;
}
So far I have something like this:
<svg>
<defs>
<clipPath id="clip-path">
<rect x="0" y="0" width="600" height="400"></rect>
</clipPath>
</defs>
<image x="0" y="0" width="100%" height="auto" xlink:href="tallimg.jpg" clip-path="url(#clip-path)">
</svg>
This almost works but the image is the wrong size because height="auto" isn't supported in SVG. Instead it interprets auto as 0. If I use height="100%" width="100%" instead, it is scaled to fit inside its container.
Is it possible to do this with pure SVG? Would I be better off layering the HTML equivalent over the top of the SVG?

Resources