Reputation: 8294
Until recently the following SVG code rendered correctly in Firefox (v25), how ever it doesn't any more (v33). All the other browsers I have tested (Chrome 33, Safari 6, IE 10).
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="1000px" width="1000px" y="0px" x="0px" version="1.1">
<defs>
<clipPath id="clip1">
<rect height="100" width="10" y="0" x="0"/>
</clipPath>
<clipPath id="clip2">
<rect height="100" width="10" y="0" x="10"/>
</clipPath>
<clipPath id="clip3">
<rect height="100" width="10" y="0" x="20"/>
</clipPath>
<symbol id="fill_texture">
<g>
<rect height="10" width="10" x="0" y="0" fill="#ff0000"/>
<rect height="10" width="10" x="3" y="5" fill="#0ff000"/>
<rect height="10" width="10" x="6" y="10" fill="#0000ff"/>
<rect height="10" width="10" x="9" y="15" fill="#ffff00"/>
<rect height="10" width="10" x="12" y="20" fill="#ff00ff"/>
<rect height="10" width="10" x="15" y="25" fill="#00ffff"/>
</g>
</symbol>
</defs>
<g id="columns">
<use id="unclipped" xlink:href="#fill_texture" width="100" height="100" x="0" y="0"/>
<use id="slot1" xlink:href="#fill_texture" clip-path="url(#clip1)" x="50" y="0"/>
<use id="slot2" xlink:href="#fill_texture" clip-path="url(#clip2)" x="100" y="0"/>
<use id="slot3" xlink:href="#fill_texture" clip-path="url(#clip3)" x="150" y="0"/>
</g>
</svg>
What I'm attempting to do is slice a prepared symbol into three parts and then use those three parts wherever I like. In Firefox 33 it seems it is applying the clip at it's original location (0,0 or 0,10 in my example) while the other browsers and previous versions of Firefox apply the clip starting from the top left corner of the use
element it is applied to.
If each clip path must be moved to match the location of the use
instead of being treated as relative to it, I can't see how a clip could ever be reused in multiple elements.
Upvotes: 3
Views: 4334
Reputation: 27544
The position of a clipping path is by default calculated in the user coordinate system of the object to which it is applied (clipPathUnits="userSpaceOnUse"
).
(You could set it to clipPathUnits="objectBoundingBox"
, but then you would have to redefine all the lengths of shapes within the clipping paths to be relative to the height and width of the shape being clipped.)
There is a simple solution to always get the effect you want: transform the coordinate system for the use elements. Instead of positioning them using x
and y
attributes, position them with a transform="translate(x,y)"
attribute. That way, the coordinate system used to position the clipping path will move with them.
Here's your fiddle updated to show the change. It's repeated below as a stack snippet, and should work as expected in all browsers.
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"
xml:space="preserve" version="1.1"
height="100px" width="400px" y="0px" x="0px" >
<defs>
<clipPath id="clip1" >
<rect height="100" width="10" y="0" x="0"/>
</clipPath>
<clipPath id="clip2">
<rect height="100" width="10" y="0" x="10"/>
</clipPath>
<clipPath id="clip3">
<rect height="100" width="10" y="0" x="20"/>
</clipPath>
<symbol id="fill_texture">
<g>
<rect height="10" width="10" x="0" y="0" fill="#ff0000"/>
<rect height="10" width="10" x="3" y="5" fill="#0ff000"/>
<rect height="10" width="10" x="6" y="10" fill="#0000ff"/>
<rect height="10" width="10" x="9" y="15" fill="#ffff00"/>
<rect height="10" width="10" x="12" y="20" fill="#ff00ff"/>
<rect height="10" width="10" x="15" y="25" fill="#00ffff"/>
</g>
</symbol>
</defs>
<g id="columns">
<use id="unclipped" xlink:href="#fill_texture"
width="100" height="100" x="0" y="0"/>
<use id="slot1" xlink:href="#fill_texture" clip-path="url(#clip1)"
transform="translate(50,0)"/>
<use id="slot2" xlink:href="#fill_texture" clip-path="url(#clip2)"
transform="translate(100,0)"/>
<use id="slot3" xlink:href="#fill_texture" clip-path="url(#clip3)"
transform="translate(150,0)"/>
</g>
</svg>
So what should happen when you use x
and y
instead of transform
? It is hard to say, because there is an inconsistency in the specifications.
The Firefox (v33) implementation makes sense from a logical application of the clipping path rules.
When you <use>
a <symbol>
, you create a new coordinate space for the content within the symbol, but the use element is still in the parent coordinate space. And since it is the use element that is being clipped, that is the coordinate that matters for matching with the clipping path. A use element with an x coordinate of 50 or more will always be outside the clipping paths you give, which don't extend past x=30.
But then, why do the other browsers position the clipping path origin at the <use>
element's (x,y) point instead of at the origin of its coordinate system? It's because of the way the specifications define how x
and y
should be implemented: as an additional transformation added to a grouping element that also has all the other attributes and styles (including clip-path
) that you specify on <use>
.
According to the specs, the following code:
<use xlink:href="#content" clip-path="url(#clip)"
x="50" y="100" width="50" height="100" />
is supposed to be rendered (assuming "content" is a <symbol>
) the same as:
<g clip-path="url(#clip)" transform="translate(50,100)">
<svg width="50" height="100" <!--viewBox and other attributes from the symbol--> >
<!-- graphics from symbol#content go here -->
</svg>
</g>
and under that representation, the clipping path should be translated to match the transform
attribute.
However, that is completely different than what would happen if you used an <image>
or <rect>
instead of <use>
, but with the exact same x
, y
, width
, height
and clip-path
attributes. For these other elements, x
and y
only define positions within the parent coordinate system, not transformations of the coordinate system, so they do change the origin of the clipping path. Which is why I would call this an unfortunate inconsistency in the specifications.
Upvotes: 5