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>
Related
I'm trying to discover the way to scale svg patterns so that the pattern is not cut by the bounds of the element using it. And yet so that pattern is beeing repeated to cover that element.
Let's say i have a 20px radius circle as a pattern.
Applying it to 80px width element:
Now the element is beeing scaled to 90px, the pattern should stretch horizontally (no need to keep aspect ratio):
And at last, scale up above 90px adds another circle occurance (95px width in this example - aspect ratio is still ignored):
I can add some scripting to achieve that effect. But i'm curious about if i can get that behavior with pure SVG.
<svg style='display: block; height: 60px; width: 95px; '>
<defs>
<pattern patternUnits='userSpaceOnUse' viewBox='0 0 20 20' width='20' height='20' id='the_pattern'>
<circle cx='10' cy='10' r='10' stroke-width='0' fill='black'></circle>
</pattern>
</defs>
<rect x='0' y='0' width='80' height='20' fill='url(#the_pattern)'></rect>
<rect x='0' y='20' width='90' height='20' fill='url(#the_pattern)'></rect>
<rect x='0' y='40' width='95' height='20' fill='url(#the_pattern)'></rect>
</svg>
The answer is no. You cannot achieve that with pure SVG. Unless by "pure SVG" you include SVGs that have embedded Javascript (ie <script> elements).
You could do it with CSS border-image though.
https://developer.mozilla.org/en-US/docs/Web/CSS/border-image
Here's an example:
Ignore the fact that I've used a diamond image here. Just replace it with a circle version of the border image file. :)
.circle-border {
border-top: 30px solid;
border-image: url('https://interactive-examples.mdn.mozilla.net/media/examples/border-diamonds.png') 30 / 20px 0 0 0;
border-image-repeat: round;
}
div {
width: 80px;
height: 0px;
margin-bottom: 50px;
}
div:nth-child(2) {
width: 90px;
}
div:nth-child(3) {
width: 100px;
}
<div class="circle-border"></div>
<div class="circle-border"></div>
<div class="circle-border"></div>
Idea
You set the dimension of the container elements in 2 passes.
Pass 1:
Set the width of the container element to the value such that the desired number of pattern elements would just fit in horizontally. That would be 80 for the first two cases and 100 for the last.
Pass 2:
Scale the container elements horizontally to the actual target widths ( 80, 90, 95 ).
In the code example below, the transform attribute specifies the scaling. Note that the translate verbs first shift the element position to the origin before scaling is applied and revert the translation thereafter (the verbs inside the `translate attribute are evaluated from right to left).
The x scaling factor is the quotient of target width and specified width, y just leaves the height as it is.
SVG Sample
<?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 width="8cm" height="4cm" viewBox="0 0 800 500" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="circlePattern" patternUnits="userSpaceOnUse"
x="0" y="0" width="20" height="20"
>
<circle cx="10" cy="10" r="10" fill="black" stroke="black" stroke-width="0"/>
</pattern>
</defs>
<!-- 1. base case 80 px width -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="200" width="80" height="20"
/>
<!-- 2. scale width to 90px -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="300" width="80" height="20"
transform="translate(100, 300) scale (1.125, 1.0) translate(-100, -300)"
/>
<!-- 3. scale width to 95px -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="400" width="100" height="20"
transform="translate(100, 400) scale (0.95, 1.0) translate(-100, -400)"
/>
</svg>
Inline Sample
The SVG parts pertaining to the question is the same as above.
.showcase {
/* background-image: url('#glyph');
background-size:100% 100%;*/
filter: url(#embedded);
}
.showcase:before {
display:block;
content:'';
color:transparent;
}
<?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 id="embedded" width="16cm" height="8cm" viewBox="0 0 800 500" version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<pattern id="circlePattern" patternUnits="userSpaceOnUse"
x="0" y="0" width="20" height="20"
>
<circle cx="10" cy="10" r="10" fill="black" stroke="black" stroke-width="0"/>
</pattern>
</defs>
<!-- 1. base case 80 px width -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="200" width="80" height="20"
/>
<!-- 2. scale width to 90px -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="300" width="80" height="20"
transform="translate(100, 300) scale (1.125, 1.0) translate(-100, -300)"
/>
<!-- 3. scale width to 95px -->
<rect fill="url(#circlePattern)" stroke="white" stroke-width="0"
x="100" y="400" width="100" height="20"
transform="translate(100, 400) scale (0.95, 1.0) translate(-100, -400)"
/>
</svg>
<div id="showcase"/>
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>
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.
I need to apply mask to a different objects.
Mask should cover whole object (visible part of it). I put the mask inside one specific place, at the top of the page, changed maskContentUnits to objectBoundingBox, so it worked perfectly.
But then a problem appeared.
I made a fiddle to illustrate the problem: http://jsfiddle.net/8qdt7vjr/1/
<body>
<svg width="0" height="0">
<defs>
<mask id="mask1" maskContentUnits="objectBoundingBox">
<ellipse cx=".5" cy=".5" rx=".5" ry=".5" fill="white" />
</mask>
</defs>
</svg>
<svg class="svg" width="200" height="150" overflow="visible">
<rect x="-50" y="-50" width="350" height="250" fill="none" stroke="green" stroke-width="2" />
<svg id="zzz" x="0" y="0" width="100%" height="100%" overflow="visible" mask="url(#mask1)">
<rect x="-50" y="-50" width="350" height="250" fill="blue" fill-opacity=".3" />
</svg>
<rect width="100%" height="100%" fill="red" fill-opacity=".1" stroke="red" stroke-width="1" />
</svg>
</body>
In this fiddle i want to apply mask to svg element with id="zzz". But inside this element there is a rect element, that increases zzz's bounding box. In the fiddle it is rect, but in my project it is an image tag. zzz has overflow set to hidden (in the fiddle I changed it to visible, so it is easier to see the problem), so I have a specific portion of image visible inside zzz.
The real problem is that zzz's inner content increases zzz's bounding box.
In the fiddle I want ellipse mask to be inside red rectangle (visible zzz's area), but it is inside green rectangle (zzz's bounding box) instead.
So the main question is: is there any way to achieve this goal? Mask will be used by many elements with different sizes and content and I do not want to clone it.
Is there any way to do this without cloning mask inside each element?
Is there any way to reduce bounding box to visible area?
Is there any way to show a part of an image without expanding parent's bounding box?
Is there any other way to do this?
Is it something like this you try to achieve?
<body>
<svg width="0" height="0">
<defs>
<mask id="mask1" maskContentUnits="userSpaceOnUse">
<ellipse cx="100" cy="75" rx="100" ry="75" fill="white" />
</mask>
</defs>
</svg>
<svg class="svg" width="200" height="150" overflow="visible">
<rect x="-50" y="-50" width="350" height="250" fill="none" stroke="green" stroke-width="2" />
<svg id="zzz" x="0" y="0" width="100%" height="100%" overflow="visible" mask="url(#mask1)">
<rect x="-50" y="-50" width="350" height="250" fill="blue" fill-opacity=".3" />
</svg>
<rect width="100%" height="100%" fill="red" fill-opacity=".1" stroke="red" stroke-width="1" />
</svg>
</body>
Maybe using userSpaceOnUse instead of objectBoundingBox can help achieve what you want... In combination with nested svg elements you can define the userspace to use and with that the part the mask is applied to...
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>