Keep svg pattern scaling when resizing svg-shape - html

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)" />

Related

How can I make this SVG mask responsive and keep the shadow at the bottom right of the circle? [duplicate]

This question already has answers here:
How to resize a svg
(2 answers)
Closed 17 hours ago.
I have the below SVG which I am using as a mask to overlay on an image, I am trying to make the circle responsive, so it shrinks on mobile devices but haven't had any luck using view box etc as it removes the mask, I am also trying to position the shadow to the bottom right of the circle, but again I am having trouble making it responsive.
Any help would be greatly appreciated.
svg {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 2000 1000" preserveAspectRatio="xMidYMin slice">
<defs>
<mask id="mask" x="0" y="0">
<rect x="0" y="0" height="100%" width="100%" fill="#fff"/>
<circle cx="50%" cy="50%" r="250" />
</mask>
<pattern id="img" patternUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<image xlink:href="https://images.unsplash.com/photo-1507181179506-598491b53db4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=299b6fae13eb39086f5bb28029c61760&auto=format&fit=crop&w=1778&q=80" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" />
</pattern>
</defs>
<path id="Shadow" d="M462.231,0c19.593,38.355,29.045,88.448,36.926,138.3,9.125,75.59-104.323,237.333-194.755,250.24C246.323,389.921,51.569,371.964,0,313.148c46.559,91.147,141.283,153.59,250.655,153.59,155.395,0,281.365-125.97,281.365-281.365A280.22,280.22,0,0,0,462.231,0" opacity="0.2" />
<rect width="100%" height="100%" mask="url(#mask)" fill-opacity="1" fill="url(#img)" />
</svg>
Putting the path inside a g container allowed me to scale the shadow relative to the mask, hope this helps someone in the future.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" preserveAspectRatio="xMidYMid slice">
<defs>
<mask id="mask" x="0" y="0">
<rect x="0" y="0" height="100%" width="100%" fill="#fff"/>
<circle cx="50%" cy="50%" r="16.5%" />
</mask>
<pattern id="img" patternUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<image xlink:href="https://images.unsplash.com/photo-1507181179506-598491b53db4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=299b6fae13eb39086f5bb28029c61760&auto=format&fit=crop&w=1778&q=80" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" />
</pattern>
</defs>
<g>
<path id="Shadow" d="m 462.231 0 c 19.593 38.355 29.045 88.448 36.926 138.3 c 9.125 75.59 -104.323 237.333 -194.755 250.24 c -58.079 1.381 -252.833 -16.576 -304.402 -75.392 c 46.559 91.147 141.283 153.59 250.655 153.59 c 155.395 0 281.365 -125.97 281.365 -281.365 a 280.22 280.22 0 0 0 -69.789 -185.373" opacity="0.2" style="transform: scale(0.3) translate(579px, 650px);" />
<rect width="100%" height="100%" mask="url(#mask)" fill-opacity="1" fill="url(#img)" />
</g>
</svg>

Red border in transparent ellipse if affected by the opacity of the container

this code
<svg viewbox="0 0 100% 100%" width="100%" height="400px">
<mask id="mask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#fff"/>
<ellipse cx="50%" cy="50%" rx="100" ry="150" stroke="red" stroke-width="10"/>
</mask>
<rect x="0" y="0" width="100%" height="100%" mask="url(#mask)" fill-opacity="0.7"/>
</svg>
return this as a result
I want to have this same result, but with a red border instead of gray in the ellipse and that this is not affected by the opacity of the rect.
I also need the inside of the ellipse to be transparent (not white) and that it is not affected by the opacity of the rect.
Is this possible?
If you are asking whether you can include a red ellipse as part of your mask, then the answer is no. Any colours in a mask are converted to alpha values.
If you want a red ellipse around your "hole", then you need to add it as a separate element on top of the rect.
body {
background-color: linen;
}
<svg viewbox="0 0 100% 100%" width="100%" height="400px">
<defs>
<!-- pull ellipse out as a reusable shape. Used by mask and for red border. -->
<ellipse id="one" cx="50%" cy="50%" rx="100" ry="150"/>
<mask id="mask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#fff"/>
<use xlink:href="#one" fill="black"/>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" mask="url(#mask)" fill-opacity="0.7"/>
<use xlink:href="#one" fill="none" stroke="red" stroke-width="10"/>
</svg>
for set order you can use tag use with the attribute xlink:href for more detail see this How to use z-index in svg elements?
add fill="#fff" tag and id="one" to ellipse and draw it again with <use xlink:href="#one"/>
<svg viewbox="0 0 100% 100%" width="100%" height="400px">
<mask id="mask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#fff"/>
<ellipse id="one" cx="50%" cy="50%" rx="100" ry="150" stroke="red" fill="#fff" stroke-width="10"/>
</mask>
<rect x="0" y="0" width="100%" height="100%" mask="url(#mask)" fill-opacity="0.7"/>
<use xlink:href="#one"/>
</svg>

SVG percentage coordinate is off

I have three SVGs nested inside another SVG. The first one is supposed to be fluid and stretched out so preserveAspectRatio is none. The other two are set to 10% and 90% for the x value. But if you resize the page you'll see they have different distance from the left and right side. Why?
I'm expecting to see the left one having the same distance from left as the right one having the distance from right.
.box {
width: 60vw;
border: 1px dashed lightgray;
}
svg {
overflow: visible;
}
<div class="box">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<svg viewBox="0 0 2 2" preserveAspectRatio="none">
<rect x="0" width="2" height="1" fill="#DDDDDD" />
</svg>
<svg x="10%" viewBox="0 0 20 20" preserveAspectRatio="xMinYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
<svg x="90%" viewBox="0 0 20 20" preserveAspectRatio="xMinYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
</svg>
</div>
Because you're starting to draw the second blue box at 90%, when you really want to end the draw at 90%. Fix this by setting the viewBox of the second blue box to:
viewBox="20 0 20 20"
Here's another solution.
The two blue SVGs are identical apart from their preserveAspectRatio attributes.
.box {
width: 60vw;
border: 1px dashed lightgray;
}
svg {
overflow: visible;
}
<div class="box">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<svg viewBox="0 0 2 2" preserveAspectRatio="none">
<rect x="0" width="2" height="1" fill="#DDDDDD" />
</svg>
<svg x="10%" width="80%" height="100%" viewBox="0 0 10 20" preserveAspectRatio="xMinYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
<svg x="10%" width="80%" height="100%" viewBox="0 0 10 20" preserveAspectRatio="xMaxYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
</svg>
</div>
For anyone as confused as me, here is the problem:
If I set the viewBox x value to half of the width 10 / 2 = 5, then I technically panned the origin to the width's center.
.box {
width: 60vw;
border: 1px dashed lightgray;
}
svg {
overflow: visible;
}
<div class="box">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<svg viewBox="0 0 2 2" preserveAspectRatio="none">
<rect x="0" width="2" height="1" fill="#DDDDDD" />
</svg>
<svg x="10%" viewBox="5 0 20 20" preserveAspectRatio="xMinYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
<svg x="90%" viewBox="5 0 20 20" preserveAspectRatio="xMinYMid">
<rect x="0" y="0" width="10" height="10" fill="#7FDBFF" />
</svg>
</svg>
</div>

SVG image - fixed height - keep ratio - slice

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>

Offset an SVG fill 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.