SVG: How to use an unclosed path as a clip path - svg

Is it possible to clip an SVG element to an unclosed path (e.g. a straight line)?
For example:
<svg>
<rect x="0" y="0" width="100" height="100" fill="blue" clip-path="url(#mask)"></rect>
<defs>
<clipPath id="mask">
<path stroke-width="10" stroke="white" d="M 0 0 L 100 100"></path>
</clipPath>
</defs>
</svg>

You can use that as a clipPath but it will clip everything because stroke doesn't count for clipPaths.
The raw geometry of each child element exclusive of rendering properties such as ‘fill’, ‘stroke’, ‘stroke-width’ within a ‘clipPath’ conceptually defines a 1-bit mask

Related

why does <g> not work in <clipPath> in svg?

The following code does not work:
<svg>
<defs>
<clipPath id="test">
<g>
<rect x="0" y="0" width="10" height="10"></rect>
</g>
</clipPath>
</defs>
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>
But this does work:
<svg>
<defs>
<clipPath id="test">
<rect x="0" y="0" width="10" height="10"></rect>
</clipPath>
</defs>
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>
In my project, I have some paths in the group tag and I want to reuse the group as a clipPath target and show the path at the same time. For example:
<svg>
<defs>
<g id="group">
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
<path d="..."></path>
</g>
<clipPath id="test">
<use xlink:href="#group" fill="#000"></use>
</clipPath>
</defs>
<!-- show the stroke of the group -->
<use xlink:href="#group" stroke="#000"></use>
<!-- at same time, clip the rect to make some animation as the background -->
<rect x="0" y="0" width="100" height="100" clip-path="url(#test)"></rect>
</svg>
Because the SVG specification says so
Content model:
Any number of the following elements, in any order:
    descriptive elements
    animation elements
    shape elements
    ‘text’
    ‘use’
Unfortunately <g> elements are not in that list.
Firefox used to support clipping <g> elements a long time ago till we noticed that we weren't a) acting per the specification above and b) compatible with other implementations so we restricted our clipPath implementation to be consistent. So if you got Chrome and Safari on board for a specification change we'd likely be OK with that.
Note that you can work around this by clipping a <use> element that points to a <g> element.

Texture mapping bitmap to SVG path

I have an ordinary rectangular bitmap which I would like to be able to use to fill a four-pointed SVG path - basically a mapped texture so that the four corners of the bitmap are mapped to the four points of the path and the rest of the image is 'warped' accordingly.
I have been able to fill an SVG rect with the same image and then transform the rect such that the bitmap is transformed with it:
<defs>
<pattern id="bmp" x="0" y="0" width="1" height="1">
<image x="0" y="0" width="100" height="100" href="mybmp.bmp"/>
</pattern>
</defs>
<rect x="0" y="0" width="100" height="100" fill="url(#bmp)" stroke="black" transform="skewX(10)"/>
When I try to use the bitmap to fill a path though it gets mapped to the bounding box of the path shape and not the four points of the path itself:
<defs>
<pattern id="bmp" x="0" y="0" width="1" height="1">
<image x="0" y="0" width="100" height="100" href="mybmp.bmp"/>
</pattern>
</defs>
<path d="M 0 0 L 100 0 L 120 80 L 50 120 Z" fill="url(#bmp)" stroke="black" />
Is it possible to get the same effect as the first example (texture properly mapped to the all corners of the rectangle) in an arbitrary path shape?
One solution is to give your pattern a viewBox so that its content image gets scaled to fit the pattern bounds.
<svg>
<defs>
<pattern id="bmp" x="0" y="0" width="1" height="1"
viewBox="0 0 100 100">
<image x="0" y="0" width="100" height="100" xlink:href="http://www.placekitten.com/100/100"/>
</pattern>
</defs>
<path d="M 0 0 L 100 0 L 120 80 L 50 120 Z" fill="url(#bmp)" stroke="black" />
</svg>
Depending on the shape of your path, you may also need to set preserveAspectRatio="xMidYMid slice". This will ensure that the pattern gets scaled to a size large enough to cover the whole path with no gaps.
<svg>
<defs>
<pattern id="bmp" x="0" y="0" width="1" height="1"
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice">
<image x="0" y="0" width="100" height="100" xlink:href="http://www.placekitten.com/100/100"/>
</pattern>
</defs>
<path d="M 0 0 L 100 0 L 120 80 L 50 120 Z" fill="url(#bmp)" stroke="black" />
</svg>

Grouping several SVG's into one object

I'm trying to reuse an SVG sprite icone into new SVG object.
The new object is just a circle that contains the icon from the sprite.
I understand that I need to use the defs tag to group some shapes together,
But i have have a problem referencing my sprite icone inside the defs tag.
sprite:
<svg style="display:none;">
<symbol id="icon_1" viewBox="0 0 54 54">
<path d="M10.6 29.3h14.5V44H10.6z" class="st0"/>
<path d="M25 29.3h14.5V44H25zm-7.2-14.7h14.5v14.7H17.8zm0 0l3.9-4m10.6 4l3.9-4m-3.9 18l3.9-3.7m-25.6 4.4l4.3-4.4m24.6 4.7l3.9-4M39.5 44l3.9-4M21.2 10.6h15M14.5 24.9h3.3m17.7.6h7.9M36.2 10v15.5m7.2.1V40" class="st0"/>
</symbol >
new object:
<svg><defs>
<g id="shape">
<circle cx="40" cy="40" r="40" fill="rgba(124,240,10,0.5)" />
<image x="0" y="0" xlink:href="#icon_1"></image>
</g>
I read that i can use image tags to reference SVG elements.
obviously i'm doing something wrong.
Basically the expected result should be a stroked circle with the icon inside
in a way that I will be able to animate the entire object
Thanks
You were close. Your main problem was that you were careless with your opening and closing tags.
Your second SVG had a stray opening <defs> element, which meant that the <g id="shape"> element was left inside the <defs> section. <defs> is for defining elements to be re-used later, and anything in a <defs> will only be drawn when referenced from elsewhere.
There were a couple of other problems.
You can't use an <image> to reference a symbol. You will need to use a <use> for that.
You will need to supply a width and height so that the symbol gets draw at an appropriate size.
<svg width="0" height="0">
<defs>
<symbol id="icon_1" viewBox="0 0 54 54">
<path d="M10.6 29.3h14.5V44H10.6z" class="st0"/>
<path d="M25 29.3h14.5V44H25zm-7.2-14.7h14.5v14.7H17.8zm0 0l3.9-4m10.6 4l3.9-4m-3.9 18l3.9-3.7m-25.6 4.4l4.3-4.4m24.6 4.7l3.9-4M39.5 44l3.9-4M21.2 10.6h15M14.5 24.9h3.3m17.7.6h7.9M36.2 10v15.5m7.2.1V40" class="st0"/>
</symbol>
</defs>
</svg>
new object:
<svg>
<g id="shape">
<circle cx="40" cy="40" r="40" fill="rgba(124,240,10,0.5)" />
<use x="0" y="0" width="80" height="80" xlink:href="#icon_1"></use>
</g>
</svg>
The image tag is designed to be used with complete images, not fractions. I guess that use is the right tag for your use case.
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1">
<symbol id="icon_1" viewBox="0 0 54 54">
<path d="M10.6 29.3h14.5V44H10.6z" class="st0"/>
<path d="M25 29.3h14.5V44H25zm-7.2-14.7h14.5v14.7H17.8zm0 0l3.9-4m10.6 4l3.9-4m-3.9 18l3.9-3.7m-25.6 4.4l4.3-4.4m24.6 4.7l3.9-4M39.5 44l3.9-4M21.2 10.6h15M14.5 24.9h3.3m17.7.6h7.9M36.2 10v15.5m7.2.1V40" class="st0"/>
</symbol>
<defs>
<g id="shape">
<circle cx="40" cy="40" r="40" fill="rgba(124,240,10,0.5)" />
<use x="2" y="-3" width="80" height="80" xlink:href="#icon_1"/>
</g>
</defs>
<use xlink:href="#shape"/>
</svg>
Also, if the sprite is in a separate file, you have to reference the symbol within that file: <use hlink:href="sprites.svg#icon1"/>.

Drawing a grid using SVG markup

I'm seeking to draw a grid onto which i will be adding a number of simple rectangles, again using SVG. The grid should fit on a single page when viewed in a browser. Suspect i'm missing something very simple but do i code the SVG (viewport and grid) for this outcome; namely a grid?
I've read advice to specify a viewbox that defines the internal coordinate system of the document's canvas; also that it's possible to set height and width attributes as percentages (?). Ideally final result (ultimately a map) is to have gridlines.
Use a pattern on a fully wide and high rectangle.
<svg width="800" height="600">
<defs>
<pattern id="tenthGrid" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="silver" stroke-width="0.5"/>
</pattern>
<pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
<rect width="100" height="100" fill="url(#tenthGrid)"/>
<path d="M 100 0 L 0 0 0 100" fill="none" stroke="gray" stroke-width="1"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)"/>
</svg>

SVG clipPath to clip the *outer* content out

Normally, the <clipPath> element hides everything that is outside the clip path. To achieve the opposite effect - that is to "cut out" something from the image - i want to use two paths in the clipPath and the clip-rule="evenodd" attribute. Basically, I want to "xor" the clip paths.
But it doesn't work. It shows the region "ORed":
<clipPath clip-rule="evenodd" id="imageclippath" clipPathUnits = "objectBoundingBox">
<rect clip-rule="evenodd" x="0.3" y="0.3" height="0.6" width="6" />
<rect clip-rule="evenodd" x="0" y="0" height="0.5" width="0.5" />
</clipPath>
<rect clip-path="url(#imageclippath)" x="0" y="0" height="500" width="500" fill="red"/>
EDIT:
My problem is that AFAIK <mask> doesn't work in iOS WebKit.
It's much easier to do what you're after with a mask, see this example. Here's the mask definition:
<mask id="maskedtext">
<circle cx="50%" cy="50%" r="50" fill="white"/>
<text x="50%" y="55%" text-anchor="middle" font-size="48">ABC</text>
</mask>
Regions that are white inside the mask will be kept, everything else will be clipped away.
Here's another example that uses clipPath instead, is a bit trickier since you need to use a path element to get the clip-rule to apply. The clipPath that uses clip-rule there looks like this:
<clipPath id="clipPath1">
<path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/>
</clipPath>

Resources