Scrollable image in SVG - 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?

Related

css rotate introduces outline when using SVG clip-path or mask

Is there a way to stop this outline in firefox?
#blob { background: red; width: 500px; height: 500px; clip-path: url(#myClip); transform: rotate(20deg);}
<div id="blob"></div>
<svg>
<defs>
<path d="M320.403196,424.677624 C426.787532,365.585154 447.310044,306.188587 433.45394,197.28033 C419.597836,88.3720737 316.997962,53.8862578 227.347416,40.9086547 C144.650118,28.9375873 104.472702,88.6407456 69.862267,131.812053 C15.52584,199.588564 48.3439099,300.905451 80.8563197,361.757908 C110.80391,417.809872 214.018859,483.770094 320.403196,424.677624 Z" id="path-1"></path>
<clipPath id="myClip"><use href="#path-1"></use></clipPath>
</defs>
</svg>
This renders correctly in other browsers, just need some work around for this firefox bug
same outline appears using either clip-path or mask
As a workaround you could rotate the clipPath instead:
#blob {
background: red;
width: 500px;
height: 500px;
clip-path: url(#myClip);
}
<div id="blob"></div>
<svg viewBox="0 0 397 409">
<defs>
<path d="M320.403196,424.677624 C426.787532,365.585154 447.310044,306.188587 433.45394,197.28033 C419.597836,88.3720737 316.997962,53.8862578 227.347416,40.9086547 C144.650118,28.9375873 104.472702,88.6407456 69.862267,131.812053 C15.52584,199.588564 48.3439099,300.905451 80.8563197,361.757908 C110.80391,417.809872 214.018859,483.770094 320.403196,424.677624 Z" id="path-1" />
<clipPath id="myClip" transform="rotate(20)" transform-origin="center">
<use href="#path-1"></use>
</clipPath>
</defs>
</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>

Responsive SVG sizing

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.

Using SVG logo as mask

I'm basing my exercise on the accepted answer in: Creating transparent text to show gradient color of underlying div
Here's my rendition in jsfiddle: http://jsfiddle.net/skrln/zSjgL/
The svg code of my logo:
<svg width="190" height="121">
<mask id="cutouttext">
<rect width="190" height="121" x="0" y="0" fill="white" />
<path id="number-two" d="M75.3,56.1c7.3-3,14.2-12.5,14.2-24c0-17.7-15.1-32.1-36.8-32.1H0v121.5h52.4c30,0,43.4-16.5,43.4-36.8
C95.8,72.3,87,59.8,75.3,56.1z M66.5,94.6h-49V79.7h0.1l27-22.1c3.5-2.8,5.3-6.1,5.3-9c0-4-3.2-7.6-8.4-7.6c-6.4,0-9.1,5.7-10.2,9
l-14.6-3.9c2.9-10.8,11.8-19.1,25.2-19.1c14.4,0,24.5,9.4,24.5,21.5c0,12.4-9,18.1-17.1,23.8l-10.4,7.3h27.6V94.6z" />
<polygon id="filler" points="190,33.9 190,0 101.6,0 101.6,121.5 190,121.5 190,87.6 141.4,87.6 141.4,74.7 177.1,74.7 177.1,46.6
141.4,46.6 141.4,33.9 " />
</mask>
<rect width="190" height="121" x="0" y="0" fill="white" mask="url(#cutouttext)" />
</svg>
The result so far:
Issue:
The mask isn't behaving the way I want to; I want the inner parts of the "B" and "E" to mask out the gray underlying div so you can see the background image like the image below:
I'm having trouble knowing what part of the logo is the and which one is the . Also I can't seem to figure out the logic behind the <mask> in the SVG.
There's nothing wrong with your SVG. You placed it on a grey background, so the bits that are masked out are grey.
What you want to do is remove the grey background from below the SVG image. There may be neater ways of doing this, but one approach is to use a table layout with the logo in one cell and the grey background in another.
Here's a JSFiddle link
HTML
<div class="gray">
<svg width="190" height="121">
<mask id="cutouttext">
<rect width="190" height="121" x="0" y="0" fill="white" />
<path d="M75.3,56.1c7.3-3,14.2-12.5,14.2-24c0-17.7-15.1-32.1-36.8-32.1H0v121.5h52.4c30,0,43.4-16.5,43.4-36.8
C95.8,72.3,87,59.8,75.3,56.1z M66.5,94.6h-49V79.7h0.1l27-22.1c3.5-2.8,5.3-6.1,5.3-9c0-4-3.2-7.6-8.4-7.6c-6.4,0-9.1,5.7-10.2,9
l-14.6-3.9c2.9-10.8,11.8-19.1,25.2-19.1c14.4,0,24.5,9.4,24.5,21.5c0,12.4-9,18.1-17.1,23.8l-10.4,7.3h27.6V94.6z" />
<polygon points="190,33.9 190,0 101.6,0 101.6,121.5 190,121.5 190,87.6 141.4,87.6 141.4,74.7 177.1,74.7 177.1,46.6
141.4,46.6 141.4,33.9 " />
</mask>
<rect width="190" height="121" x="0" y="0" fill="white" mask="url(#cutouttext)" />
</svg>
<div></div>
</div>
CSS
.body {
background: #550000 url('http://sciencelakes.com/data_images/out/7/8788677-red-background-wallpaper.jpg');
display: block;
height: 500px;
margin: 0;
}
.gray {
display:table-row;
width:100%;
height:121px;
}
.gray div, .gray svg {
display:table-cell;
}
.gray div {
background:gray;
width:100%;
}

Resources