I have a SVG that needs to have a fixed height so its not super big when the width is 2000+ pixels (widescreens...)
The clipping mask should always be visible and the background image should be sliced and not be stretchend, i tried several things but nothing seems to work.
This is what i have now:
https://codepen.io/bucky208/pen/OEqbPp
div {
width: 100%;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1381.5" preserveAspectRatio="none" style="display: block; position: absolute; width: 100%;height: 400px;">
<g id="clipgroup">
<defs>
<polygon id="mask" points="0,572.1 0,1381.3 1024,1040.5 1024,337.6 0,0"/>
</defs>
<clipPath id="mask_1_">
<use xlink:href="#mask" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#mask_1_);">
<image style="overflow:visible;" width="331" height="444" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" transform="matrix(3.1119 0 0 3.1111 -3.0528 -2.604167e-04)"></image>
</g>
</g>
</svg>
</div>
Do i need another wrapper around everything? How do i restore the image ratio?
Kind regards and a big thank you for everyone trying to help.
In order to get image fills to fill their container but preserve the original aspect ratio, a filter combined with objectBoundingBox sizing and preserveAspectRatio is your friend. The following code does what I think you want:
svg {
background: red;
}
#svgcont {
position: absolute;
width: 100%;
}
<div id="svgcont">
<svg x="0" y="0" width="100%" height="800px">
<defs>
<filter id="image-fill-nostretch" primitiveUnits="objectBoundingBox">
<feImage x="0" y="0" width="1" height="1" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" preserveAspectRatio="xMidYMid slice"/>
<feComposite operator="in" x1="SourceGraphic"/>
</filter>
<clipPath id="mask_1_" clipPathUnits="objectBoundingBox">
<polygon id="mask" points="0,0.5 0,1 1,0.75 1,0.25 0,0"/>
</clipPath>
</defs>
<g clip-path="url(#mask_1_)">
<rect width="100%"height="100%" x="0%" y="0%" filter="url(#image-fill-nostretch)"/>
</g>
</svg>
</div>
Related
I am having a problem with setting up shadow for a svg with mask applied to it. This is the code and jsfiddle: http://jsfiddle.net/3kxnmhfL/
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" height="100%" width="100%" viewBox="0 0 100 100">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<circle cx="50%" cy="50%" r="50%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
<!-- <rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" /> -->
</mask>
</defs>
<circle cx="50%" cy="50%" r="50%" fill="red" mask="url(#cut-off-bottom)" filter="url(#shadow)" />
</svg>
</div>
I'd like the shadow to appear only around svg circle and not containing div.
What could be the reason for shadow not showing up?
SVG filters have a "filter region". The filter region defines the area of pixels that the browser uses to store the result of the filters. The default filter region is the bounds of the element (that the filter is applied to) plus a margin around it to allow for filter elements that have results larger than the original element.
By default, that margin is 10% of the width and height, applied to all four sides. However in your case, a stdDeviation of "4" causes the blur to extend further than that 10% margin allowance. The result is a clipped blur, even though the viewBox has been enlarged enough to cater for the full blur.
To fix, this, you just need to bump up the filter region size. An allowance of 20% seems to work okay:
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
In addition, I've simplified the SVG by getting rid of the unnecessary mask. I also tweaked the viewBox to include the part of the blur that extends to the left.
.watch-video-svg {
width: 500px;
height: 500px;
background: linen;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="-5 0 120 120">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
</defs>
<circle cx="50" cy="50" r="50" fill="red" filter=url(#shadow) />
<polygon points="31,20, 31,77, 80,50" fill="white"/>
</svg>
</div>
You need to adjust your mask. Since your element is already a circle you don't need a circle in the mask, a rect to fill all the width/height is enough.
You need to also adjust the viewBox to leave some space for the shadow:
.watch-video-svg {
width: 50px;
height: 50px;
}
<div class="watch-video-svg">
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" viewBox="0 0 120 120">
<defs>
<filter id="shadow">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<mask id="cut-off-bottom">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<polygon points="31,20, 31,77, 80,50" fill="black"/>
</mask>
</defs>
<circle cx="50" cy="50" r="50" fill="red" mask="url(#cut-off-bottom)" filter=url(#shadow) />
</svg>
</div>
I have a situation where I have a SVG graphic whose width is a percentage of the viewport, and whose height is fixed. Inside the graphic I have an SVG image element which I want to always fill its immediate container without distorting, much as would be achieved by using CSS background-size: fill.
How can I achieve this using my SVG?
Here's a minimal setup of the problem (and a codepen):
<svg width=100% height=300>
<rect width=100% height=300 stroke="blue" stroke-width=30 fill="transparent" />
<image xlink:href="//unsplash.it/500/300" width=100% height=100% />
</svg>
In the above snippet, I need the image to fill the entire container without distorting (in this instance, the container being the root SVG), regardless of viewport width.
I can't switch to regular HTML because the graphic I'm working on has an SVG clipping mask applied to the image element.
I found this question, which seems close to what I'm asking, but I don't think answers my question:
How can I make my embedded svg image stetch to fill a container?
In hopes of avoiding the XY problem, here's the actual SVG graphic I'm working on. And the (fixed-height, variable-width) result I'm trying to achieve:
Svg scaling with image
If you add a viewbox to the svg the svg knows how to scale.
Now adding the same viewBox ratio as the svg makes it so the svg will always have the same scale as the image.
You can always scale the image to its inside the viewBox.
Then it will always scale with the svg.
svg {
border: 2px solid black;
}
<svg width="100%" height="100%" viewBox="0 0 500 300">
<image width="500" height="300" xlink:href="//unsplash.it/500/300"/>
</svg>
To keep the height of the image unchanged, set the fixed value of the viewport height and preserveAspectRatio = "none"
<svg width="100%" height="300" viewBox="0 0 500 300" preserveAspectRatio = "none">
<image width="500" height="300" xlink:href="//unsplash.it/500/300"/>
</svg>
UPD
The proportions of the picture will not change, the height remains fixed when using preserveAspectRatio="xMinYMin slice"
<svg width="100%" height="300" viewBox="0 0 500 300" preserveAspectRatio="xMinYMin slice">
<image width="100%" height="300" xlink:href="//unsplash.it/500/300" />
</svg>
UPD2
I looked your codepen Like the idea. I've finished it with your permission. Modified some parameters
<svg width="100%" height="400" preserveAspectRatio="xMinYMin slice">
<defs>
<mask id="clipping">
<rect width="100%" height="400" fill="white"/>
<ellipse cx="50%" cy="120%" rx="75%" ry="37%"/>
</mask>
<linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="white"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
</defs>
<rect width="100%" height="400" fill="orange" mask="url(#clipping)" transform="translate(-20 12) rotate(-2.5)" />
<image xlink:href="https://unsplash.it/1000/500" width="100%" height="400" mask="url(#clipping)" transform="translate(0 -10)" />
<rect width="100%" height="400" fill="url(#Gradient1)" mask="url(#clipping)" transform="translate(0 -10)" opacity="0.25" />
</svg>
I ended up using two SVGs and an image which used a clipping mask defined in the first SVG to clip it down. I was able to make the SVGs scale by only defining a viewbox, not a width and height. I opted to use a fixed aspect ratio instead of a fixed height, percentage width, as I had originally planned.
body > * {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
img {
clip-path: url(#clipper);
}
<svg viewBox="0 0 1000 500">
<defs>
<symbol id="arch" viewBox="0 0 1000 400">
<path d="M0 0 H 1000 V 400 Q 500 300, 0 400" />
</symbol>
<clipPath id="clipper" clipPathUnits="objectBoundingBox">
<path d="M0 0 H 1 V 1 Q .5 .75, 0 1" />
</clipPath>
<linearGradient id="Gradient1" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="#87AFBF"/>
<stop offset="100%" stop-color="#002855"/>
</linearGradient>
</defs>
<use href="#arch" width="100%" y="-40" fill="#F9B000" transform="translate(-20 12) rotate(-2.5)" />
</svg>
<img src="//unsplash.it/1000/400" alt="">
<svg viewBox="0 0 1000 500">
<use href="#arch" y="-50" fill="url(#Gradient1)" opacity="0.75" />
</svg>
CodePen
I try to draw a svg in my HTML Code to have a specific path/object with an background image.
The object should be a little bit responsive (using bootstrap), but filled with the image and the image should preseve its ratio.
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="none" >
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="images/headerBackground.png" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>
You can see it here in the live demo:
https://liveweave.com/N5nib6
https://jsfiddle.net/zyyvd86g/
Maybe anybody can help? I hope the problem is clear enough.
You can use max-width:100% for the svg element and div element wrap on this svg.
You can get the responsive image
div {
width: 80%;
height: auto;
margin: 0 auto;
}
svg {
max-width: 100%;
height: auto;
}
<div>
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="none" >
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="images/headerBackground.png" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>
</div>
IMO opinion, the simple solution to your problem is to use a different preserveAspectRatio on your SVG.
Using preserveAspectRatio="none" is going to stretch your SVG and cause problems.
I'm assuming you want to keep the shape of the "swoop" on the bottom of your path. Correct?
If that is the case, you might prefer to use
preserveAspectRatio="xMidYMax slice"
instead. This scales the SVG up to fill the full width of the SVG viewport, whilst keeping the aspect ratio the same, and keeping the bottom of the SVG viewBox on screen.
<svg width="100%" height="370px" viewBox="0 0 1148.942 598.47" preserveAspectRatio="xMidYMax slice">
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="1153" height="680">
<image xlink:href="http://lorempixel.com/1153/680/" x="0" y="0" width="1153" height="680" />
</pattern>
</defs>
<path fill="url(#img1)" d="M1145.237,3.395H3.379v592c0,0,247.108-160.416,1141-99L1145.237,3.395z"/>
</svg>
I have a svg-pattern applied to a polygon. It's working fine.
When I set another size on the svg-polygon, I don't want to scale the pattern.
I've tried all combinations I can think of with viewBox, patternUnits and patternContentUnits. The goal is to make the polygon work responsibly e.g. scale with it's parent element. Can anyone point me in the right direction?
<svg width="1000" fill="#ccc" viewBox="0 0 1440 60">
<defs>
<pattern id="pattern" x="0" y="0" width="900" height="600" patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse">
<svg x="0" y="0" width="900.4" height="600" viewBox="0 0 900.4 600">
<!-- pattern code -->
</svg>
</pattern>
</defs>
<polygon points="0,0 1440,0 1440,20 0,60" x="0" y="0" stroke="#bbb" fill="url(#pattern)" />
</svg>
<svg width="500" fill="#ccc" viewBox="0 0 1440 60">
<defs>
<pattern id="pattern" x="0" y="0" width="900" height="600" patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse">
<svg x="0" y="0" width="900.4" height="600" viewBox="0 0 900.4 600">
<!-- pattern code -->
</svg>
</pattern>
</defs>
<polygon points="0,0 1440,0 1440,20 0,60" x="0" y="0" stroke="#bbb" fill="url(#pattern)" />
</svg>
full example: https://codepen.io/anderssonola/pen/QqxKjJ
I solved temporarily by applying the pattern to a <rect>and then use css clip-pathto create the polygon and the pattern does not scale. I still would prefer to solve it with pure svg, since IE does not support the css clip-path.
.clip {
background: gray;
clip-path: polygon(0 0, 100% 0, 100% 30%, 0% 100%);
margin-bottom: 1em;
}
.clip.half {
width: 50%;
}
svg {
display: block;
height: 50px;
width: 100%
}
<div >
<svg>
<defs>
<pattern id="pattern" x="0" y="0" width="900" height="600" patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse">
<svg x="0" y="0" width="900.4" height="600" >
<!-- pattern code -->
</svg>
</pattern>
<rect width="100%" height="50" fill="url(#pattern)" id="pattern-md"/>
</defs>
</svg>
</div>
<div class="clip">
<svg>
<use href="#pattern-md"/>
</svg>
</div>
<div class="clip half" >
<svg>
<use href="#pattern-md" />
</svg>
</div>
Working example: https://codepen.io/anderssonola/pen/oGyBMj/
You could always scale the pattern and size the polygon appropriately e.g.
<polygon transform="scale(2)" points="0,0 720,0 720,10 0,30" stroke="#bbb" fill="url(#pattern)" />
Is it possible to offset the pattern in an svg element by a certain amount?
The example below uses a pattern of circles that is embedded in a <g> element that has an x="70" offset. Unfortunately the offset only 'cuts' away a part of the element without moving the fill pattern.
html, body, svg {
margin: 0;
width: 100%;
height: 100%;
}
<svg class="gFlow" id="main" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern height="64" id="grid" patternunits="userSpaceOnUse" width="64">
<circle cx="32" cy="32" fill="orange" r="5"></circle>
</pattern>
</defs>
<rect fill="url(#grid)" height="100%" width="100%" x="70"></rect>
</svg>
Don't offset the rectangle, offset the pattern. You can specify the origin (offset) of a pattern using the x and y attributes. It doesn't matter if the offset is positive or negative, the pattern will still fill the element completely.
html, body, svg {
margin: 0;
width: 100%;
height: 100%;
}
svg {
border: solid 1px black;
}
<!-- Pattern with no offset -->
<svg class="gFlow" id="main" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern height="64" id="grid" patternUnits="userSpaceOnUse" width="64">
<circle cx="32" cy="32" fill="orange" r="5"></circle>
</pattern>
</defs>
<rect fill="url(#grid)" height="100%" width="100%"></rect>
</svg>
<!-- Pattern moved right by half the pattern width (32) -->
<svg class="gFlow" id="main" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern height="64" id="grid" patternUnits="userSpaceOnUse" width="64"
x="32" y="0">
<circle cx="32" cy="32" fill="orange" r="5"></circle>
</pattern>
</defs>
<rect fill="url(#grid)" height="100%" width="100%"></rect>
</svg>
Note: SVG attributes are technically case sensitive. That's changing, but you should use the correct case for backwards compatibility. patternunits should be patternUnits.