I am trying to create an svg object with top, middle and bottom sections, where top and bottom are of fixed height, and fixed should occupy all the space between top and bottom (depending on the height of the browser window or other containing object). Here is some code that does NOT work:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="mainSvg" xmlns="http://www.w3.org/2000/svg" version="1.1"
width="100%" height="100%"
viewbox="0 0 1000 500"
preserveAspectRatio="none"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="background-color: #cccccc;">
<svg id="top" x="0" y="0" width="100%" height="50"
style="overflow: visible;">
<line x1="0" y1="0" x2="100%" y2="100%"
style="stroke-width:5; stroke: red;"/>
<line x1="0" y1="100%" x2="100%" y2="0"
style="stroke-width:5; stroke: red;"/>
</svg>
<svg id="middle" x="0" y="50" width="100%"
style="overflow: visible;">
<line x1="0" y1="0" x2="100%" y2="100%"
style="stroke-width:5; stroke: blue;"/>
<line x1="0" y1="100%" x2="100%" y2="0"
style="stroke-width:5; stroke: blue;"/>
</svg>
<svg id="bottom" x="0" y="100%" width="100%" height="50"
style="overflow: visible;">
<line x1="0" y1="0" x2="100%" y2="-100%"
style="stroke-width:5; stroke: red;"/>
<line x1="0" y1="-100%" x2="100%" y2="0"
style="stroke-width:5; stroke: red;"/>
</svg>
</svg>
If you run that, you will see that top appears at the top and bottom is at the bottom (with the assistance of a little "-100%" hack).
Middle starts below top (y="50") but because its height is defaults to "100%", it overlaps bottom and indeed extends below the available rendering area. Clearly, its height needs to be "100% - 50 - 50", but there is no way to express that. Css has a nice feature where you can say
height: calc(100% - 50 - 50);
but in svg "height" is not a property that can be specified within css. So there I am stuck.
Note that in the intended application, the whole thing will be embedded within an html page, the insides of the sections will contain complicated svg, and I require that the whole thing can be resized vertically with automatic scaling of "middle", mainly because that will be necessary in order to print the page on some given size of paper.
Making such a structure with three 'div's in HTML is easily done, and at a pinch I could create three such 'div's and embed three pieces of svg within them, but that would be messy. I hope there is an easier way.
Related
I have a three-part SVG. I would like to stretch the left and right parts infinitely, but keep the middle part intact.
Here’s the illustration.
I think my answer lies somewhere around viewbox tag preserveAspectRatio attribute of SVG. I haven't worked with them though, and could use a nudge in the right direction.
Here is the code for the unstretched SVG, which I think I need to somehow augment with viewboxes to achieve the desired stretching.
<svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<line y1="0.5" x2="32" y2="0.5" stroke="#FF0000"/>
<line x1="16" y1="8.5" x2="32" y2="8.5" stroke="#00FF00"/>
<line x1="16" y1="16.5" x2="48" y2="16.5" stroke="#0000FF"/>
</svg>
Here is the Codepen for code experiments: https://codepen.io/jaanus1/pen/aboQpeB
Set: width="100%" height="100%" preserveAspectRatio="none"
<svg width="100%" height="100%" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<line y1="0.5" x2="32" y2="0.5" stroke="#FF0000"/>
<line x1="16" y1="8.5" x2="32" y2="8.5" stroke="#00FF00"/>
<line x1="16" y1="16.5" x2="48" y2="16.5" stroke="#0000FF"/>
</svg>
Update
I tried to make a new option.
Two lines red and green are included in one SVG block which is scaled.
The green line enters another SVG block, which has a fixed size: width="48" height="17"
To keep all lines the same width when zooming in, I added the property vector-effect ="non-scaling-stroke"
.one {
position:relative;
}
.two {
position:absolute;
top:35%;
left:50%;
}
<div class="one">
<svg width="100%" height="5%" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<line vector-effect="non-scaling-stroke" y1="0.5" x2="32" y2="0.5" stroke="#FF0000"/>
<line vector-effect="non-scaling-stroke" x1="16" y1="16.5" x2="48" y2="16.5" stroke="#0000FF"/>
</svg>
<div class="two">
<svg width="48" height="17" viewBox="0 0 48 17">
<line x1="16" y1="8.5" x2="32" y2="8.5" stroke="#00FF00"/>
</svg>
</div>
</div>
I can create a svg with line at any given percentage "0%-100%" so that the rounded borders (in pixels) are not included in the "percentage width" with the help of calc calc(100% - 25px)
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<rect fill="lightblue" y="0" x="0" width="100%" height="50" rx="25" ry="25"></rect>
<g class="percentage">
<line class="100pct" x1="calc(100% - 25px)" x2="calc(100% - 25px)" y1="0" y2="50" stroke="red" stroke-width="4"></line>
</g>
</svg>
But the question is, can this same svg be created without calc for legacy browsers?
I can use transform and translate to take one rounded side into account, but I can't figure out how to limit the width / add some kind of margin.
The percentage change, so one shared translate gets me only halfway there, here the red 100% line is out of bounds:
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<rect fill="lightblue" y="0" x="0" width="100%" height="50" rx="25" ry="25"></rect>
<g class="percentage" transform="translate(25, 0)">
<line class="0pct" x1="0%" x2="0%" y1="0" y2="50" stroke="blue" stroke-width="4"></line>
<line class="100pct" x1="100%" x2="100%" y1="0" y2="50" stroke="red" stroke-width="4"></line>
</g>
</svg>
Is there really any browser supporting the above syntax? If yes, it is in violation of even the SVG2 spec:
A future specification may convert the ‘x1’, ‘y1’, ‘x2’, and ‘y2’ attributes to geometric properties. Currently, they can only be specified via element attributes, and not CSS.
(And since calc() is a CSS function, it can only be used in CSS contexts.)
What will work in all browsers supporting SVG is to combine x/y values with transforms; unitless = px values go to the transform attribute, units (percentages) go to the x/y attribute.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<rect fill="lightblue" y="0" x="0" width="100%" height="50" rx="25" ry="25"></rect>
<g class="percentage" >
<line class="0pct" x1="100%" x2="100%" y1="0" y2="50" transform="translate(-25, 0)" stroke="red" stroke-width="4"></line>
</g>
</svg>
In addition to the SVG 1.1 transform attribute, there is also the CSS transform property and its 2D functions which are implemented quite fairly (exeption: IE and Edge < 17). They must use unit identifiers, and should also support nested calc() functions. I have no compatibility data for that combination, but there are also no bug reports mentioned in caniuse.com.
What currently does not work is using the CSS transform syntax as a presentation attribute (CSS transform spec is not yet implemented in that regard), so you need to use them in style attributes.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50">
<rect fill="lightblue" y="0" x="0" width="100%" height="50" rx="25" ry="25"></rect>
<g class="percentage" stroke="red" stroke-width="4" >
<line class="0pct" x1="0" x2="0" y1="0" y2="50"
style="transform:translate(calc(0 * (100% - 50px) + 25px))" />
<line class="50pct" x1="0" x2="0" y1="0" y2="50"
style="transform:translate(calc(0.5 * (100% - 50px) + 25px))" />
<line class="100pct" x1="0" x2="0" y1="0" y2="50"
style="transform:translate(calc(1 * (100% - 50px) + 25px))" />
</g>
</svg>
As you can see, the positional value is no longer a percentage (multiplying pixels with percentages does not work), but a fraction of 1. I hope that works for you.
I'm not sure if this is the best approach. I'm trying to make an SVG that has parts that scale and parts that are fixed. It looks like this:
When the web page loads, I don't know what the height of the container for it will be but I know the width. I want the joining lines to scale based on the height, but keep the box with the plus centered like this:
I've played around with the line settings for x1, y1, etc., but I can't figure out a way to do it without resorting to javascript. Is SVG not the best option here? Here's what I have so far:
<svg class="s2">
<line x1="50%" y1="0" x2="50%" y2="10%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- top joining line -->
<g id="square" x="50%" y="50%" width="16px" height="16px">
<line x1="5" y1="8" x2="11" y2="8" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- plus horizontal line -->
<line x1="8" y1="5" x2="8" y2="11" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- plus vertical line -->
<rect x="4" y="4" width="8" height="8" style="fill:transparent;stroke:rgba(0,0,0,.5);"></rect>
</g>
<line x1="50%" y1="90%" x2="50%" y2="100%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- bottom joining line -->
<line x1="90%" y1="50%" x2="100%" y2="50%" style="stroke:rgba(255,0,0,.9);stroke-width:1px;"></line> <!-- right joining line -->
</svg>
Would javascript be my only option here? I tried using values like
calc(50% - 5px)
for the line positioning but it looks like it's not supported. If it was that would fix the problem.
For the solution, you have to combine two techniques:
masking parts of the lines, and
combine positioning in px with CSS translations in percentage
You start by positioning your rect centered on the coordinate origin, giving sizes in pixels. The joining lines are first simply drawn without interruption from -50% to +50%. Then the parts behind your central rect are masked out, the sizing again in px.
Finally, everything is moved by transform:translate(50%, 50&) to fill the SVG. It is important to note that this is the CSS transform property that can have units, while the SVG transform presentation attribute can only have unitless numbers. It therefore has to be written in a style attribute (or in a stylesheet).
#outermost {
transform:translate(50%, 50%);
}
g line {
stroke:rgba(255,0,0,.9);
stroke-width:1px;
}
g rect {
fill:none;
stroke:rgba(0,0,0,.5);
}
<svg xmlns="http://www.w3.org/2000/svg" class="s2" width="24" height="100">
<mask id="cp">
<rect x="-50%" y="-50%" width="100%" height="100%" fill="white"></rect>
<rect x="-6" y="-6" width="12" height="12" fill="black"></rect>
</mask>
<g id="outermost">
<g mask="url(#cp)">
<line x1="0" y1="-50%" x2="0" y2="50%"></line>
<line x1="0" y1="0" x2="50%" y2="0"></line>
</g>
<line x1="-3" y1="0" x2="3" y2="0"></line>
<line x1="0" y1="-3" x2="0" y2="3"></line>
<rect x="-4" y="-4" width="8" height="8"></rect>
</g>
</svg>
Currently using an SVG <pattern> element with a bunch of <line> elements will cause it to have a sort of tapered-off edge. I've tried a bunch of different CSS stylings to get around this, but nothing's really worked. Here's the code to a circle with a stitch masked on it:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="500" width="500">
<defs>
<pattern id="stripe" patternUnits="userSpaceOnUse" width="20" height="20">
<line x1="0" y1="0" x2="20" y2="20" />
</pattern>
<mask id="mask">
<rect height="500" width="500" style="fill: url(#stripe)" />
</mask>
<style>
#stripe line {
fill: white;
stroke: white;
stroke-linecap: square;
stroke-linejoin: miter;
stroke-width: 10px;
}
g {
mask: url(#mask);
stroke-linecap: square;
stroke-linejoin: miter;
}
circle {
fill: green;
}
</style>
</defs>
<g>
<circle cx="250" cy="250" r="200" style="fill: rgba(0, 255, 0, 0.2);" />
</g>
</svg>
And here's a fiddle of what this looks like. No combination of stoke-linecap and stroke-linejoin has worked for me. Do I instead need to draw a full <path> across the entire mask?
Thanks for any help.
Well I know that this answer isn't far off 2 years late, but I stumbled across this question while researching another problem and thought I'd throw in my 2 cents for anyone who might come across it.
The problem here is as highlighted by Duopixel: the pattern just doesn't tile right.
Your solution certainly masks the problem by overlaying two non-tiling patterns to hide the non-tiling corners, but if you make the pattern wider and add additional lines offset from one another, and offset so as to ensure they never overlap a corner of the tile you can create a functioning pattern. You can up the stroke width on this all the way up without any problems with the corners.
<pattern id="stripe" patternUnits="userSpaceOnUse" width="40" height="20">
<line x1="-10" y1="0" x2="10" y2="20" />
<line x1="10" y1="0" x2="30" y2="20" />
<line x1="30" y1="0" x2="50" y2="20" />
</pattern>
See this fiddle
I actually like the pattern made by the original problem though! I might have to find a use for it somewhere :)
Woo! What a ride.
After seeing Duopixel's answer, I got started on a trail. I didn't know it was possible to achieve this effect until I understood the bounding box that applies to patterns.
Googling brought me to this mailing list answer which didn't make much sense at first until the original author returned with gained insight (sorry, too many links). I looked back at the answer and saw potential in solving this problem.
Solution:
You have to overlay two patterns on-top of eachother in the right coordinates!
Code: (demo - http://jsfiddle.net/66UDU/)
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="500" width="500">
<defs>
<pattern id="stripe1" class="stripe" patternUnits="userSpaceOnUse" width="20" height="20">
<line x1="0" y1="0" x2="20" y2="20" />
</pattern>
<pattern id="stripe2" class="stripe" patternUnits="userSpaceOnUse" x="6" y="6" width="20" height="20">
<line x1="0" y1="0" x2="20" y2="20" />
</pattern>
<mask id="mask">
<rect height="500" width="500" style="fill: url(#stripe1)" />
<rect height="500" width="500" style="fill: url(#stripe2)" />
</mask>
<style>
.stripe line {
fill: white;
stroke: white;
stroke-width: 4px;
}
g {
mask: url(#mask);
}
circle {
fill: rgba(0, 255, 0, 0.25);
}
</style>
</defs>
<g>
<circle cx="250" cy="250" r="200" />
</g>
</svg>
=)
That's an interesting problem. It looks like a line-cap issue but the actual problem is that the line corners of your pattern lie outside of the coordinates. Here is a diagram to understand what is happening:
You can either make the pattern larger with <pattern id="stripe" patternUnits="userSpaceOnUse" width="30" height="30"> or move the coordinates of your lines. In a cursory search I couldn't find any directive that would allow your pattern to overlap or display the overflow, but someone else might know of a workaround.
The post is old, but as I searched for a solution maybe someone also needs another solution like me.
As described above, a diagonal pattern has problems, thats why I created a straight horizontal line pattern and tranformed it with patternTransform="rotate(45)".
Then only 1 <line /> is needed instead of 2 overlapping ones.
Example:
<pattern id="stripe45" className="stripe-pattern" patternUnits="userSpaceOnUse" patternTransform="rotate(45)" width=".2" height=".2">
<line x1="0" y1=".1" x2=".2" y2=".1" />
</pattern>
When i try to render this svg sample, the line is bluring automatically and has 2px height:
<svg xmlns="http://www.w3.org/2000/svg" width="500px" height="500px" >
<line x1="100" x2="400" y1="250" y2="250" stroke="black" />
</svg>
But when Y coordinates becomes 250.5, all is OK - line has 1px height.
It would be a solution (adding 0.5 pixels), but I need to use scale transform on elemets. On transformed elemets the problem again.
How to solve it?
Thanks.
By blurring you mean anti-aliasing. Try the crisp-edges rendering mode, e.g.:
<svg xmlns="http://www.w3.org/2000/svg" width="500px" height="500px" >
<line x1="100" x2="400" y1="250" y2="250" stroke="black" shape-rendering="crispEdges" />
</svg>