Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply clipPath to transformed g element

Tags:

svg

I'm trying to clip a circle so that it only shows for the part that falls within certain bounds. However, the circle is within a g element that is transformed. When I apply the clip path to either the g element or the path within this element ("g.site" or "g.site path") the circle complete gets clipped off. Short example showing my problem:

<svg width="600" height="600">
  <defs>
    <clipPath id="myClip">
      <path d="M435.1256860398758,144.76407538624122L419.76193083948306,273.83328117717105L469.9933509829825,301.0396981292212L483.3234271019269,296.67464757752555L535.23683445551,247.72472220603692L574.3496211247055,127.3184557867296Z"
      />
    </clipPath>
  </defs>
  <g id="voronoi">
    <g id="cells">
      <path class="cell" d="M435.1256860398758,144.76407538624122L419.76193083948306,273.83328117717105L469.9933509829825,301.0396981292212L483.3234271019269,296.67464757752555L535.23683445551,247.72472220603692L574.3496211247055,127.3184557867296Z"
      />
    </g>
    <g id="sites">
      <g class="site" transform="translate(483.29548177370367,267.14072835257724)" clip-path="url(#myClip)">
        <path fill="rgba(0, 255, 0, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z"
        />
      </g>
      <g class="site" transform="translate(483.29548177370367,267.14072835257724)">
        <path fill="rgba(0, 0, 255, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z"
        />
      </g>
    </g>
  </g>
</svg>

A working demo of my problem can be found in this fiddle: http://jsfiddle.net/xRh6A/

I added two circles. The first one is clipped off (because the clip-path attribute is set), the second is shown but (obviously) not clipped.

I suppose this is related to the fact that the clip-path is defined in absolute terms while the circle element has local coordinates and is then transformed. Can I use the clipPath with a transformed group or do I have to either change the clip path or the circle path in order to make them match?

Edit I solved it by placing the "sites" with absolute coordinates. However, this meant that I couldn't use d3.svg.arc (which is generating the code in the simplified example I attached) because it creates arcs in a local coordinate system.

I'd still be interested to see if it could be solved otherwise as well.

like image 511
Bertjan Broeksema Avatar asked Jan 22 '13 10:01

Bertjan Broeksema


People also ask

How do I use clipPath in CSS?

The clip-path property in CSS allows you to specify a specific region of an element to display, with the rest being hidden (or “clipped”) away. There used to be a clip property, but note that is deprecated. The most common use case would be an image, but it's not limited to that.

What is clipPath in SVG?

The <clipPath> SVG element defines a clipping path, to be used by the clip-path property. A clipping path restricts the region to which paint can be applied. Conceptually, parts of the drawing that lie outside of the region bounded by the clipping path are not drawn.

What is clipPath?

The clip-path CSS property creates a clipping region that sets what part of an element should be shown. Parts that are inside the region are shown, while those outside are hidden.


1 Answers

Your translation on the g element is affecting how the clipPath is rendered. You have 2 options:

  1. Add the clip-path attribute to a static parent element. You can then apply your translation on the child element without it affecting the clipPath rendering.

  2. Apply the inverse translation on the clipPath element. I've never implemented this method, but I read about it here: https://stackoverflow.com/a/16166249/3123187. This option requires that you update the clipPath tranform each time you change the g transform.

In your case, you already have a parent g element so you can add the clip-path attribute there, if you intend to apply the clipPath to every element in your g#sites element.

<g id="sites" clip-path="url(#myClip)">
  <g class="site" transform="translate(483.29548177370367,267.14072835257724)">
    <path fill="rgba(0, 255, 0, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z" />
  </g>
  <g class="site" transform="translate(483.29548177370367,267.14072835257724)">
    <path fill="rgba(0, 0, 255, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z" />
  </g>
</g>

(jsfiddle: http://jsfiddle.net/SWyeD/)

If you only intend for that clipPath to be applied to the first circle, you'd just add an intermediate container element.

<g id="sites">
  <g clip-path="url(#myClip)">
    <g class="site" transform="translate(483.29548177370367,267.14072835257724)">
      <path fill="rgba(0, 255, 0, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z" />
    </g>
  </g>
  <g class="site" transform="translate(483.29548177370367,267.14072835257724)">
    <path fill="rgba(0, 0, 255, 0.5)" d="M0,30A30,20 0 1,1 0,-30A30,20 0 1,1 0,30M0,1A1,1 0 1,0 0,-1A1,1 0 1,0 0,1Z" />
  </g>
</g>

(jsfiddle: http://jsfiddle.net/bdB65/)

like image 162
bfin Avatar answered Nov 16 '22 01:11

bfin