Sizing pattern element based on viewport - svg

I have a large svg image that will be scrolled. I would like to add a tiled background image behind all the elements. This is currently leaving me with two problems:
I haven't found a way to keep a background image fixed, and not scroll it with the svg elements on top of it.
Assuming that's not possible, I wanted to size the tile elements to occupy 20% of the viewable area rather than 20% of the total svg size.
<svg width="3560.00px" height="1350.00px" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="G-BG" width="20.00%" height="20.00%">
<image xlink:href="https://static.vecteezy.com/system/resources/thumbnails/000/301/800/small/jfyo_waxi_190121.jpg" x="0" y="0" width="20.00%" height="20.00%"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#G-BG)" fill-opacity="0.20"/>
<circle fill="green" cx="200" cy="200" r="100.00"/>
</svg>
If you run this little example, it sets a background composed of 5 x 5 copies of some arbitrary image, but the repeat interval of the tiling is based on 20% of the full svg size. If I try and use vw/vh units then the pattern size seems to go to zero, and so disappear.
My ideal choice would be to have a fixed 5 x 5 image that doesn't scroll, but the CSS background support doesn't seem to work for for svg.
My second choice would be to have a scrolling background image, but where there are 5 x 5 tiles visible at any one time.
Can either of these goals be achieved?

Not sure if there is anything here that might help?
html,body{
height: 100%;
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrapper{
width: 100%;
height: 100%;
}
.wrapper svg{
background-color: 'red';
top: 0;
left:0;
position: fixed;
width: 100%;
height: 100%;
}
<div class="wrapper">
<svg xmlns="http://www.w3.org/2000/svg" >
<pattern id="pattern" width="0.2" height="0.25" viewBox="0 0 1 1" preserveAspectRatio="xMidYMid meet">
<image width="0.5" height="0.5" href="https://static.vecteezy.com/system/resources/thumbnails/000/301/800/small/jfyo_waxi_190121.jpg" />
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern)"></rect>
</svg>
</div>

Related

Half of line stroke is hidden on the SVG edge

If SVG line touches SVG edge, half of the line stroke will be outside of SVG and will be hidden (see example of line chart below, last tick at the right has half-width tickness).
What are the techincs to avoid it? Is there a way to scale out SVG a little bit, so the SVG image will be smaller and don't touch SVG borders? Something like adding padding?
I'm using non-fixed aspect ratio as height:1rem and width:100%, as it's supposed to take all the place available.
The viewbox scaling doesn't work
svg {
display: block;
}
.chart {
width: 300px;
border: 1px solid #ccc;
}
<div class="chart">
<svg height="1rem" width="100%" class="text-left" viewBox="-5% 0 105% 1rem">
<line x1="25%" y1="5%" x2="25%" y2="25%" stroke="#6B7280" stroke-width="2"></line>
<line x1="50%" y1="5%" x2="50%" y2="25%" stroke="#6B7280" stroke-width="2"></line>
<line x1="100%" y1="5%" x2="100%" y2="25%" stroke="#6B7280" stroke-width="2"></line>
<rect x="0%" y="25%" width="15.138888888888888%" height="50%" fill="#111827"></rect>
</svg>
</div>

How to dynamically size an SVG pattern to the size of it's children?

I'm trying to create a text based SVG fill pattern that is dynamic (any text could be used).
I want the text to repeat horizontally and vertically, without having to define width/height of the text object in the pattern definition. This Answer provided a lot of info regarding patternUnits, but it wasn't enough to answer my specific question. The pattern seems to require either absolute width/height (doesn't work with dynamic content) or relative % values that are based on the SVG canvas.
Is it possible for a pattern definition to be sized dynamically to fit its children's bounding box (supporting arbitrary text), while using userSpaceOnUse for the pattern's children to set pixel values?
The usecase is consuming user-provided text, so predefined widths/sizes do not work; the solution needs to be agnostic to the text content.
svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="GPattern" x="0" y="0" width="100%" height="100%" patternUnits="userSpaceOnUse">
<text id="text" x="0" y="34" width="100%" height="100%"
style="font-family: Arial;
font-size : 34;
font-weight: 800;
stroke : #000000;
fill : #00ff00;
"
>This Text Should Be Dynamic</text>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#GPattern)"/>
</svg>
Goal Output:
Right now the solution I am considering would be to use JS to calculate an absolute width based on content string.length and manually edit the pattern's width attribute, but would prefer if SVG can calculate this automagically.
Leveraging the answer in https://stackoverflow.com/a/13873631/1577447, you can use Javascript to adjust the size of your pattern.
You can trigger adjustPatternSize() every time your code changes. I’ve baked in the default values in case you want to run this in a context where JS isn’t available.
svg{width:100%;height:100%;}
body{height: 100vh;}
body, html{padding:0;margin:0;}
input { position: absolute; left: 10%; bottom: 10%; }
<input id="textInput" type="text" value="This Text Is Dynamic">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="GPattern" x="-4" y="-1" width="483" height="46" patternUnits="userSpaceOnUse">
<text id="text" x="0" y="34" width="100%" height="100%"
style="font-family: Arial;
font-size : 34;
font-weight: 800;
stroke : #000000;
fill : #00ff00;
"
></text>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#GPattern)"/>
<script type="text/javascript">
const padding = 4
const text = document.getElementById("text");
const rect = document.getElementById("GPattern");
const input = document.getElementById("textInput");
input.oninput = handleInput;
function handleInput(e) {
adjustPatternSize(e.target.value)
}
function adjustPatternSize(string) {
text.textContent = string;
const bbox = text.getBBox();
rect.setAttribute("x",bbox.x - padding);
rect.setAttribute("y",bbox.y - padding );
rect.setAttribute("width",bbox.width + 2*padding);
rect.setAttribute("height",bbox.height + 2*padding);
}
adjustPatternSize("This Text Is Dynamic");
</script>
</svg>

SVG Animation - Reverse and stop after 50px from top

How can I reverse the SVG animation ... Need to animate from TOP to BOTTOM?
And I want to stop the round after 50px from top (from 0 to 50px it has to animate and stop)
svg {
border: 3px solid #eee;
display: block;
margin: 1em auto;
}
p {
color: #aaa;
text-align: center;
}
<svg width="100" height="200" viewBox="0 0 200 500">
<path id="motionPath" fill="none" stroke="#ED1E25" stroke-width="4" stroke-miterlimit="10" stroke-dasharray="10,8" d="M53.738,2243.583
c0,0,53.198-53.916,58.558-133.25S64.321,1976.33,49.655,1954.33s-44.331-55.33-44-122.664
c0.111-22.688,9.666-77.666,48.666-129.333c24.376-32.293,40.667-85.68,41.667-118.678c0,0,3.667-63.012-34.333-114.345
c-10.717-14.478-56.003-66.333-47.001-133c3.597-26.633,11.017-64.334,44.509-107s57.491-91.004,53.133-144.337
c-2.348-28.726-11.975-80.665-59.975-133.665C31.307,928.106-6.345,864.978,8.988,791.978c1.793-8.538,15.666-62.331,44.666-95.998
s47.334-113.995,41.334-147.995c-2.515-14.252-5-58.736-54.042-116.589c-16.472-19.432-39.65-81.737-20.958-134.07
c8.677-24.293,20-51.077,26.5-60.577s34-51.25,38-84.25c1.444-11.916,10.5-87-31-144.584"/>
<circle id="circle" r="10" cx="0" cy="0" fill="tomato" />
<animateMotion
xlink:href="#circle"
dur="5s"
begin="0s"
fill="freeze"
repeatCount="1">
<mpath xlink:href="#motionPath" />
</animateMotion>
</svg>
You can reverse the animation with:
keyPoints="1;0" keyTimes="0;1" calcMode="linear"
in the animationMotion definition. With these attributes you can also stop the animation and go back to the start. I just added a black line at ~ 50px (50px + the margin on top) and modified the keyPoints that the circle turns at this line. It's
keyPoints="1;0.952;1" keyTimes="0;0.5;1" calcMode="linear"
in your case. That's not the cleanest way, because it's not exactly after 50px, but if you don't want to add a new path, which has the needed length of your animation path, you have to test a little bit.
Working fiddle: https://jsfiddle.net/df4qt9zz/
Moreover keep in mind that SVG is scalable, so the 50px will be around 100px if the size of the SVG is getting bigger.

prevent path deformation made by preserveAspectRatio="none"

I'd like to prevent <path> elements from stretching caused bypreserveAspectRatio="none" on <svg> tag.
I've found nearly perfect solution adding this attribute to <path> elements:
vector-effect="non-scaling-stroke"
But I need them to be scalable with transform="scale()" which is also disabled or more precisely their stroke-width are fixed and not scalable. So, my question is:
Is there any way to prevent stretching but not zooming?
Ok, to make it more clear: https://codepen.io/lukydorny/pen/aNYOdW
I would like to shape a path by different viewBox width and height (2nd image) but I need horizontal line to be of same width as vertical(3rd image). And then I wish I was able to scale it exactly the same way as original path(4th image) which is disabled as you can see in the last image.
Is there any way how to do it?
Yes, you can achieve this simply by defining the SVG element like this:
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="100%"
height="100%"
viewBox="0 0 1920 1080"
style="display:block">
... your SVG paths code here
</svg>
Notice the "viewBox" size should be the size of your "svg document" (maximum x and maximum y values).
This preserves the aspect ratio you define; however, be aware that your paths created should comply with the size you define, else it may cause problems.
To "zoom" the whole graphic, simply change with width & height of the SVG element, or its parent element, or use "scale()" inside the SVG, or with JavaScript as you normally would.
Would this option work?
svg {
border: solid 1px
}
<div title='original path' style='float:left;'>
<svg width=192px height=54px viewBox='0 0 200 200'>
<path style="stroke-linejoin: round; stroke-linecap: round; opacity: 1; stroke: rgb(50, 122, 97); stroke-width: 8px; fill: none;" d="m30 68.709l5,0,4.111,0,3.444,0,3.555,0,4.111,0.071,5,0.214,5.222,0.428,5.222,0.571,5,0.642,4.778,0.571,4.778,0.428,4.778,0.214,5.111,0.071,4.778,0,4.333,0,3.667,0,3.444,0,3.333,0,3.222,0,3,0,2.556,0,2.111,-0.071,1.889,-0.214,1.778,-0.499,1.778,-0.785,1.445,-1.07,1.111,-1.284,0.778,-1.498,0.556,-2.069,0.444,-2.64,0.222,-3.211,0.111,-2.997,0,-2.569,0,-1.998,0,-1.998,0,-2.283,0,-2.64,0,-2.497,0,-2.069,0,-1.569,-0.556,-1.284,-1.556,-1.07,-3,-0.785,-3.778,-0.499,-4.222,-0.214,-4.222,-0.071,-4.889,0,-5.333,0,-6,0,-5.778,-0.071,-5.778,-0.214,-5.556,-0.428,-5.556,-0.571,-5.889,-0.571,-5.889,-0.428,-5.778,-0.214,-4.445,-0.143,-3.333,-0.143,-2.444,-0.214,-2.333,-0.214,-2.333,-0.214,-2.222,-0.214,-2,-0.214,-1.778,-0.214,-1.667,-0.214,-1.778,-0.071,-1.778,0.357,-1.667,1.926,-1.222,3.781,-0.778,5.494,-0.333,5.28,-0.111,4.495,0,3.568,0,3.496,0,4.21,0,4.923,0,5.494,0,4.78,0,3.996,0,3.282,0,3.14,0,3.354,0,3.496,0.111,3.568,0.333,2.854,0.667,2.069,1.111,1.427,1.445,1.213,1.778,0.999,1.778,0.713,1.889,0.499,1.889,0.499,2.111,0.499,2.889,0.571,4.111,0.571,5.778,0.713,6.778,0.713,7.333,0.642,7,0.428,6.333,0.214,5.778,0.071,5.667,0,6.111,0,7.111,0,8.111,0,8.556,0,7.333,0,5.667,-0.071,4.444,-0.143,4.333,-0.285,4.556,-0.285,4.667,-0.285,4.556,-0.143,4.333,-0.143,4,-0.143,3.333,-0.214,2,-0.143,0,0"></path>
</svg>
</div>
<div title='original path' style='float:left;'>
<svg width=192px height=108px viewBox='0 0 200 200'>
<path style="stroke-linejoin: round; stroke-linecap: round; opacity: 1; stroke: rgb(50, 122, 97); stroke-width: 8px; fill: none;" d="m30 68.709l5,0,4.111,0,3.444,0,3.555,0,4.111,0.071,5,0.214,5.222,0.428,5.222,0.571,5,0.642,4.778,0.571,4.778,0.428,4.778,0.214,5.111,0.071,4.778,0,4.333,0,3.667,0,3.444,0,3.333,0,3.222,0,3,0,2.556,0,2.111,-0.071,1.889,-0.214,1.778,-0.499,1.778,-0.785,1.445,-1.07,1.111,-1.284,0.778,-1.498,0.556,-2.069,0.444,-2.64,0.222,-3.211,0.111,-2.997,0,-2.569,0,-1.998,0,-1.998,0,-2.283,0,-2.64,0,-2.497,0,-2.069,0,-1.569,-0.556,-1.284,-1.556,-1.07,-3,-0.785,-3.778,-0.499,-4.222,-0.214,-4.222,-0.071,-4.889,0,-5.333,0,-6,0,-5.778,-0.071,-5.778,-0.214,-5.556,-0.428,-5.556,-0.571,-5.889,-0.571,-5.889,-0.428,-5.778,-0.214,-4.445,-0.143,-3.333,-0.143,-2.444,-0.214,-2.333,-0.214,-2.333,-0.214,-2.222,-0.214,-2,-0.214,-1.778,-0.214,-1.667,-0.214,-1.778,-0.071,-1.778,0.357,-1.667,1.926,-1.222,3.781,-0.778,5.494,-0.333,5.28,-0.111,4.495,0,3.568,0,3.496,0,4.21,0,4.923,0,5.494,0,4.78,0,3.996,0,3.282,0,3.14,0,3.354,0,3.496,0.111,3.568,0.333,2.854,0.667,2.069,1.111,1.427,1.445,1.213,1.778,0.999,1.778,0.713,1.889,0.499,1.889,0.499,2.111,0.499,2.889,0.571,4.111,0.571,5.778,0.713,6.778,0.713,7.333,0.642,7,0.428,6.333,0.214,5.778,0.071,5.667,0,6.111,0,7.111,0,8.111,0,8.556,0,7.333,0,5.667,-0.071,4.444,-0.143,4.333,-0.285,4.556,-0.285,4.667,-0.285,4.556,-0.143,4.333,-0.143,4,-0.143,3.333,-0.214,2,-0.143,0,0"></path>
</svg>
</div>

How to position an svg to vertically fill a variable height div?

I've created some svg graphics that I would like to set as backgrounds in some variable height divs. I'm trying to use them as a sprite, so one svg file has a few groups, each with its own id, and I'm using those ids to specify which background is rendered in each div.
Here's a link to a (non-working) JSFiddle.
HTML:
<svg width="0" height="0">
<defs>
<!-- BLUE GRAPHIC -->
<g id="blue_background">
<polygon fill="#C8D9E5" points="[...in the fiddle...]"/>
</g>
<!-- GREEN GRAPHIC -->
<g id="green_background">
<path fill="#49B974" d="[...in the fiddle...]"/>
</g>
</defs>
</svg>
<div class="box">
<h1>asdasdasd</h1>
<h1>asdasdasd</h1>
<h1>asdasdasd</h1>
<svg class="svg1" viewBox="0 0 600 200" >
<use xlink:href="#blue_background" width="100%" height="100%"></use>
</svg>
</div>
<div class="box">
<h1>asdasdasd</h1>
<h1>asdasdasd</h1>
<h1>asdasdasd</h1>
<svg class="svg2" viewBox="0 0 200 200">
<use xlink:href="#green_background" width="100%" height="100%"></use>
</svg>
</div>
Ideally the graphic would be aligned with the right side, and stretch to fill the div's height.
Thanks
Since you're describing your SVGs as "backgrounds", I assume you don't need them to push text out of the way.
In that case, I would recommend using absolute positioning for the SVGs (relative to the "box" containers). With absolute positioning, a height of 100% is valid even if the parent element doesn't have fixed height.
.box {
position: relative;
width: 400px;
border: 1px solid #ddd;
}
svg {
position:absolute;
height:100%;
top:0; right:0;
z-index:-1;
}
http://jsfiddle.net/svag2/1/

Resources