SVG clipPath - clipped area offset and size problem - svg

Please see the yellow rectangle, this is exactly the same as the rectangle clipping the image.
However image is clipped smaller and moved right.
The image is just one element, but imagine few more elements which all need to be clipped to a shape of a yellow rectangle.
I know I can fix this by wrapping the <image> element (and any more elements) inside a <g> element and applying the clipPath to this <g> element.
Is it possible to fix this issue by modifying just the clipPath part, without touching the rest of the svg structure?
<defs>
<clipPath id="clipPath">
<path d="M150-750 L150,750 L-150,750 L-150,-750Z" transform="matrix(1,0,0,1,152.5,770.5)"></path>
</clipPath>
</defs>
<path fill="#ffff00" d="M150-750 L150,750 L-150,750 L-150,-750Z" transform="matrix(1,0,0,1,152.5,770.5)"></path>
<image x="-1632" y="-1224" width="3264" height="2448" preserveAspectRatio="none"
xlink:href="https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_960_720.jpg"
transform="matrix(0.3529,0,0,0.3529,246.2554,998.5607)"
style="clip-path: url(#clipPath);"></image>
Please see the jsfiddle here.

Everything happens because you transform the path and the image with a different value. I've removed the transforms and changed the viewBox value so that the clipping path falls inside the svg canvas.
Also I've removed the height of the svg element because I wanted to keep the same aspect ratio as the viewBox.
Please take a look. Let me know if this is what you need.
<svg width="305" style="overflow: hidden; position: relative;"
viewBox="-200 -800 750 3964" preserveAspectRatio="xMinYMin">
<defs>
<clipPath id="clipPath">
<path id="test" d="M150-750 L150,750 L-150,750 L-150,-750Z" ></path>
</clipPath>
</defs>
<use xlink:href="#test" fill="#ffff00" ></use>
<image x="-1632" y="-1224" width="3264" height="2448" preserveAspectRatio="none"
xlink:href="https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_960_720.jpg"
style="clip-path: url(#clipPath);"></image>
</svg>

Related

Why does an svg with viewbox of different ratio to dimensions renders element in center?

I am trying to understand why in the following svg the element appears in the center
<svg xmlns="http://www.w3.org/2000/svg"
width="400" height="100"
viewBox="0 0 100 100"
style="outline: 1px solid green"
>
<rect x="0" y="0" width="75" height="100" fill="#2222FF" stroke="green"/>
</svg>
My understanding is that viewBox would take the box defined by the coordinates 0,0 - 100,100 on the svg and map it to the 400x100 image. So that should give me a top-to-bottom rectangle that reaches 3/4 of the way across. At the very least I would expect the rectangle to be all the way on the left.
I cannot for the life of me understand why the rectangle here appears in the center. What is going on?
Because the default preserveAspectRatio is xMidYMid.

How to automatically create the minimal size of the viewbox which fits for the complete content?

I have a simple or complex SVG graphic. For example a rotated rectangle.
Without calculating you cannot know the minimal size of the viewbox, where the graphic fits into completely.
<svg viewBox="0 0 30 30">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</svg>
The result is, that the graphic does not fit into the viewbox.
Is there any method, how to get an the minimal size of the viewbox, where the graphic is shown completely?
Ideally I do not want to declare a size/ratio of a viewbox. I just want that the minimal size is a result of the content of the SVG graphics.
Is there any disadvantage, when I do not declare the viewBox attribute at all?
Thanks for your help.
One way to do it is wrapping the transformed rectangle in a <g> element and then get the value of the bounding box for theG. Next you use the values of the bounding box (BB) to reset the viewBox of theSVG. I hope it helps.
// the bounding box for the wrapping g
let BB = theG.getBBox();
theSVG.setAttributeNS(null, "viewBox", `${BB.x} ${BB.y} ${BB.width} ${BB.height}`)
svg{border:1px solid}
<svg id="theSVG" viewBox="0 0 30 30" width="300">
<g id="theG">
<rect x="20" y="0" width="100" height="20" transform="rotate(45)" fill="black" />
</g>
</svg>

SVG or HTML text that scales to fully fit a container so it stretches, bot vertically and horizontally, disregarding aspect ratio

I need to make text automatically stretch in both dimensions, to fill a container. It will distort.
This shows the the container space in red
This shows what a long name would normally resize to put in that space and maintaining aspect ratio
.
This shows what my client wants to happen
.
I would prefer to use SVG but I will work with what works.
I have searched for a solution to the best of my abilities but all seem to either refer to maintaining aspect ratio or stretching text when the page or viewbox changes dimensions.
That's quite a broad question, but yes you can do it with svg, I'll let you implement it though since you didn't provided anything to chew on.
The key point is to set your svg's preserveAspectRatio to "none":
svg{
height: 100vh;
width: 50vw;
}
body{
margin:0;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 40" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
Hello, out there
</text>
</svg>
</div>
If your text is already part of an SVG (as it appears in your example), you will probably need to use a nested <svg> element.
<svg width="400" height="400">
<rect width="400" height="400" fill="rebeccapurple"/>
<!-- rect representing area that our text has to squeeze into -->
<rect x="20" y="50" width="200" height="50" fill="white"/>
<!-- x y width height match above rect -->
<!-- viewBox values need to match text bounds -->
<svg x="20" y="50" width="200" height="50"
viewBox="0 8 244 28" preserveAspectRatio="none">
<text x="0" y="35" font-family="Verdana" font-size="35">
HELLO THERE
</text>
</svg>
</svg>
The hardest part is workoing out the correct values for viewBox. It needs to match the bounds of the (normal unsqueezed) text.

SVG mask is clipping stroke on masked element

I have a completely vertical <path> with a thick stroke-width applied. I would like to add a mask (i.e. mask="url(#...)") to it, however when I do, (how do i put this?) the stroke is ignored when computing the visible area. Here's a code snippet:
function toggleMask() {
var path = $('path');
if (path.attr('mask')) {
path.removeAttr('mask');
} else {
path.attr('mask',"url(#test)");
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div><button onclick="toggleMask()">toggle mask</button></div>
<svg width="400" height="400">
<defs>
<mask id="test">
<rect
width="100%"
height="100%"
x="0"
y="0"
fill="white">
</rect>
<circle r="20" cx="35" cy="80" fill="black"></circle>
</mask>
</defs>
<path
d="M30,30L30,300"
stroke-width="40"
stroke="black"></path>
<path
d="M50,30L100,300"
stroke-width="40"
stroke="black"></path>
</svg>
I expected the applied mask to look something like this:
Thanks in advance!
Add maskUnits="userSpaceOnUse"to your mask and it'll work as you want.
https://codepen.io/nerdmanship/pen/XazJVR
The reason is that when the maskUnits attribute is unspecified it defaults to the objectBoundingBox value, which means that the mask is applied to the area inside a target element's bounding box. The bounding box of a completely vertical or horizontal path has the width or height of 0. The result is that the mask is applied to a total area of 0 pixels of the target element.
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/maskUnits

SVG border appears trimmed

I think I am missing an apparent offset issue in my very first svg here, The top and left borders are tirmmed (pointed by red arrow), Also if something like nested <g> or <symbol> is possible pleas let me know, Thanks. (screenshot in FF27).
The simplified code and a fiddle
<svg>
<defs>
<symbol id="ringCenters" style="fill:black;">
<circle cx="50" cy="50" r="2" />
/*...more circles*/
</symbol>
<symbol id="ring1" class="rings">
<path d="M99.9746,51.5943
A50 50 0 1 0
62.5254,98.4057"
stroke="green" />
<path d="M99.9746,51.5943
A50 50 0 0 1
62.5254,98.4057"
stroke="red" />
</symbol>
/*...more rings*/
</defs>
<use xlink:href="#ringCenters" x="10" y="10" />
/*...more rings*/
</svg>
.rings{fill:none;}
svg {
width:600px;
height:300px;
}
The stroke around a shape is always centered on the exact geometric border of the shape, and so extends beyond the shape by half the stroke width.
If for any reason you don't want to use overflow:visible, another option is therefore to just adjust the positions of your shape so that you have a bit of padding on the edges, equal to half the stroke width. (Of course, that assumes that the stroke-width will be the same every time you use the symbol.)
Note that you have to adjust the position of the <path> within the <symbol>, not just the position of the <symbol> within your SVG. That's because each reference to a symbol element is drawn as if it was a nested <svg> element within it's own viewport, with a fixed height, width, and "viewBox" coordinate system. You're not setting those attributes on the symbol, so they end up as the defaults for nested SVGs: width and height equal to 100% of the parent, and a default coordinate system with (0,0) at the top left corner of the viewport.
That "nested SVG" then gets positioned so that it's top left corner is at the (x,y) position specified in the <use> element, but as far as the drawing content within the symbol is concerned, the rectangular edges of that viewport are the edges of the drawing surface. Unless, of course, you specifically allow overflow, as #helderdarocha suggested.
(By the way, the symbols-are-drawn-as-nested-SVGs idea is probably why you needed to specify svg{ overflow:visible;} for Firefox, although it really should work by setting the property on the symbols directly; I'd call that a Firefox bug.)
On your other question: <g> elements can be nested multiple times without problem. For <symbol> elements, it's not so clear. The specs say that symbols are much like <g> elements except (a) they have viewports and (b) the symbol is not drawn directly, it is only available for reference by a <use> element.
Now, that suggests that <symbol>s can be nested like <g> elements. And on Chrome it works. On Firefox, not so much. I assume what is happening is that when Firefox copies the internal content of the outer <symbol>, it treats the internal <symbol> elements as if they were just symbol definitions, not as if they were renderings of those symbols.
I.e. code like this
<svg>
<defs>
<symbol id="complicated">
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
</symbol>
</defs>
<use xlink:href="#complicated"/>
</svg>
Gets rendered as if it was
<svg>
<defs>
<symbol id="complicated">
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
</symbol>
</defs>
<g> <!-- The <use> element is drawn as if it was a <g> element -->
<svg> <!-- The <symbol> element is drawn as if it was a nested <svg> -->
<symbol id="firstPart"> <!-- the internal symbol is copied as-is -->
<path d="...."/>
</symbol>
<symbol id="secondPart"> <!-- but symbols aren't drawn directly! -->
<path d="...."/>
</symbol>
<svg>
</g>
</svg>
...and that shouldn't really be rendered as anything at all. This, I wouldn't call a Firefox "bug", just a literal interpretation of the specs, which don't give any special treatment to nested symbol elements.
There is a way to get symbols to nest, however, and that's to use <use> elements within the symbol definition:
<svg>
<defs>
<symbol id="firstPart">
<path d="...."/>
</symbol>
<symbol id="secondPart">
<path d="...."/>
</symbol>
<symbol id="complicated">
<use xlink:href="#firstPart"/>
<use xlink:href="#secondPart"/>
</symbol>
</defs>
<use xlink:href="#complicated"/>
</svg>
Here's your fiddle updated with that structure: http://jsfiddle.net/vGNMu/6/
Half of your stroke is outside the viewBox. You can avoid clipping by using:
svg, symbol {
overflow: visible;
}
Or adding an overflow="visible" attribute to each symbol.
Your updated JSFiddle

Resources