1

I'm pretty sure this is either incredibly easy or one of those disappointing things to add to my SVG wish list:

Suppose I have a rect like so:

<rect x="0" y="0" height="500" width="800 fill="gold" stroke="red" stroke-width="16" />

And I also have a `circle, like this:

<circle cx="800" cy="0" r="250" fill="#c06" stroke="#930" stroke-width="12" />

Kind of close to this fiddle.

  1. Is it possible to define the circle as visible above the rect but also define the rect as the "visible area" for the circle so that the circle is clipped (not visible) where it does not intersect/overlap with the rect

  2. If this is possible, is it further possible (assuming it's not the default behavior) to define the "visible" area as the filled area of the rect so that the stroke is still considered outside of the visible area and the circle appears to be under the stroke but above the fill of the rect (like a shape under a picture frame)?

Anthony
  • 36,459
  • 25
  • 97
  • 163

3 Answers3

3

An alternative approach that doesn't use a <clipPath> (since you hate them so much :)

 <svg version="1.1">
    
        <rect x="6" y="6" height="250" width="400"
              fill="gold" stroke="red" stroke-width="6"/>         
        <svg x="6" y="6" height="250" width="400" overflow="hidden">
           <circle cx="394" cy="-6" r="125"
                   fill="#c06" stroke="#930" stroke-width="6"/>
        </svg>
    
    </svg>
Alexandr_TT
  • 13,635
  • 3
  • 27
  • 54
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
2

Like this then...

<svg version="1.1">
<defs>
    <clipPath id="frame">
        <use xlink:href="#r"/>
    </clipPath>
</defs>

<rect id="r" x="6" y="6" height="250" width="400" 
      fill="gold" stroke="red" stroke-width="6"/>
<circle cx="400" cy="0" r="125" fill="#c06" stroke="#930" stroke-width="6" 
            clip-path="url(#frame)"/>
</svg>

For part 2 just draw the stroke as an additional rect on top.

<svg version="1.1">
<defs>
    <clipPath id="frame">
        <use xlink:href="#r"/>
    </clipPath>
</defs>

<rect id="r2" x="6" y="6" height="250" width="400" 
      fill="gold"/>
<circle cx="400" cy="0" r="125" fill="#c06" stroke="#930" stroke-width="6" 
            clip-path="url(#frame)"/>
<rect id="r" x="6" y="6" height="250" width="400" 
      fill="none" stroke="red" stroke-width="6"/>
</svg>
Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • 1
    That is clever, in a cheeky way. Will test. – Anthony Oct 28 '13 at 16:19
  • 1
    Have you been speaking to my Mother? She said as much about me when I was 7. – Robert Longson Oct 28 '13 at 16:26
  • I've been too busy trying to explain to my mother why she needs to stop using tabled-layouts and direct mysql inserts. She called me "snarky" the other day. I digress. Your trick is quite neat and works except that SVG, as usual, favors the center of the stroke as where a shape starts, not the inside edge. I thought I'd be so smart and apply a `scale(stroke-width)` to the `clipPath`, but was quickly reminded that `scale` units are a percentage, not the view units. Any thoughts on how to reliably decrease the clipPath `rect` to the inner area? (further warning, it also has rounded corners). – Anthony Oct 28 '13 at 16:59
  • I suspect you'll need to clip to a custom rect or path. – Robert Longson Oct 28 '13 at 17:12
  • and we're back to my svg wishlist. I went ahead and took my rounded rectangle backdrop and hide everything else; copied it to just below and removed stroking and set a different fill so I could find which numbers would fit right along the inside edge so it looked like butter on toast. Found the numbers (basically subtracting half the stroke-length in some places and add the full stroke-length in others, etc) , looked good, made that my clipPath, and of course...there is a slight hairline gap. i could almost cry and I just know buried in that damn spec there is some convoluted solution. – Anthony Oct 28 '13 at 17:25
  • Give us a jsfiddle we can improve for you then. – Robert Longson Oct 28 '13 at 17:31
  • I figured out what the problem is, I think. Using the suggested workaround for forcing the stroke orientation ( http://stackoverflow.com/questions/7241393/can-you-control-how-an-svgs-stroke-width-is-drawn ) I got a somewhat cleaner implementation than using a hard-coded clipPath shape. Then I made the colors loader and zoomed into that hairline split. The issue is that the clipped circle path is clipped at the inner arc of a rounded rectangle, so it's kinda like `(⁀`, opposing arcs trying to meet at one clean point. Fiddle: http://jsfiddle.net/crazytonyi/GmrpW/ – Anthony Oct 28 '13 at 18:19
1

Blah, knew it would be easier to find if I wrote it out as a question. The solution (though not my favorite and a better one is welcome) is to use a clipPath element. Using the example from the fiddle in question:

<g>
    <clipPath id="frame">
        <rect x="6" y="6" 
              height="250" width="400" stroke-width="6"/>
    </clipPath>
</g>

<rect x="6" y="6" height="250" width="400" 
      fill="gold" stroke="red" stroke-width="6"/>
<circle cx="400" cy="0" r="125" fill="#c06" stroke="#930" stroke-width="6" 
            clip-path="url(#frame)"/>
Anthony
  • 36,459
  • 25
  • 97
  • 163
  • What aspects of this solution are you unhappy about? – Robert Longson Oct 28 '13 at 14:46
  • I would prefer if I could set the clip-path to the shape in the viewport and not have to define it separately with a designated `clipPath` element. I'm sure I could define it once and then reuse it in both contexts via `use` but that's really just saving bytes, whereas I would prefer to semantically show that the circle is clipped (or framed) by the lower rect. – Anthony Oct 28 '13 at 14:56