13

I'd like to draw a shape which has holes in it such that I can fill the shape it and not have the holes filled with that colour (leave them transparent).

According to the W3 path spec:

Compound paths (i.e., a path with multiple subpaths) are possible to allow effects such as "donut holes" in objects.

Can somebody please give a very simple example of how to perform this with a vector path in Raphael?

Thanks very much.

robertc
  • 74,533
  • 18
  • 193
  • 177
doctororange
  • 11,670
  • 12
  • 42
  • 58

4 Answers4

19

This turns out to be quite straightforward if you know the trick. For example this doesn't work:

paper.path("M 50 50 L 50 150 L 150 150 L 150 50 z" + 
          " M 75 75 L 75 125 L 125 125 L 125 75 z")
.attr("fill", "#f00");

But this does work*:

paper.path("M 50 50 L 50 150 L 150 150 L 150 50 z" +
          " M 75 75 L 125 75 L 125 125 L 75 125 z")
.attr("fill", "#f00");

The difference is that for the donut to appear the the inner path has to have it's vertices drawn in reverse order to the outer path (ie. draw one clockwise, the other anti-clockwise). A tidbit I found on the text.xml.svg.devel archives.

(*) At least, it works in Chrome, Opera and Firefox 4.0 beta, but not in 3.6

robertc
  • 74,533
  • 18
  • 193
  • 177
  • I am trying to use this technique to fill a circular doughnut, any idea how to get the circle function to draw in reverse so that it fills correctly? – Neil Feb 09 '12 at 15:06
  • @Neil As far as I'm aware you have to use paths to make this work. – robertc Feb 09 '12 at 20:37
  • @robertc thanks I did indeed achieve this with paths, thanks for the tip. – Neil Feb 13 '12 at 17:42
  • I was about to ask the same question. The result (with differents coordinates) can be seen here : http://jsfiddle.net/cWHdk/2/ – challet Jun 19 '13 at 13:15
  • Hi @ robertc. I am looking for the opposite of your solution please. And here is my question: http://stackoverflow.com/questions/20847767/how-to-change-an-svgs-complex-paths-that-have-duplicated-paths-for-each-line-to – user3117671 Aug 06 '14 at 02:35
4

To make this work in Firefox 3.6, you need to close the hole; i.e. make the coordinates join back to themselves when defining the inner boundary. Curiously, this doesn't appear necessary for the outer boundary.

paper.path("M 50 50 L 50 150 L 150 150 L 150 50 z" +
          " M 75 75 L 125 75 L 125 125 L 75 125 L 75 75 z")
.attr("fill", "#f00");

Just a quick note to follow up on the comment - the clockwise/counter-clockwise concept might seem strange at first, but it's pretty standard throughout GIS / CAD technologies.

Ben
  • 1,902
  • 17
  • 17
  • Hi @ Ben. I am looking for the opposite of your solution please. And here is my question: http://stackoverflow.com/questions/20847767/how-to-change-an-svgs-complex-paths-that-have-duplicated-paths-for-each-line-to – user3117671 Aug 06 '14 at 02:33
3

I think the correct way to do this is setting the attribute "fill-rule" to the value "evenodd". Take a look at the svg spec:

Don't try to set it with "Raphael.Element.attr()". It doesn't work. I use the jQuery.attr() function instead:

// assuming paper is a Raphael.Paper object
path = paper.path('Mx,y{some path commands for the main shape}Z'
                  +'Mx,y{some path commands for the hole}Z'
);

// this doesn't work
path.attr({'fill-rule': 'evenodd'});
// neither this
path.attr({fillRule: 'evenodd'});

// if you inspect the object returned by paper.path
// you can see it has a reference to the DOM element
console.debug(path)

// so a bit of jQuery and it's done
$(path[0]).attr('fill-rule', 'evenodd');

I have used this on complex paths with successful results.

Sergey K.
  • 24,894
  • 13
  • 106
  • 174
laconbass
  • 31
  • 1
  • Just wanted to give a non-jQuery update to this: `path[0].setAttribute('fill-rule', 'evenodd');` works – Howard Mar 16 '16 at 10:03
2

For anyone looking to do circular donuts, great easy plugin Raphael-donut-plugin

Gist:

Raphael.fn.donut = function(x, y, innerRadius, outerRadius) {
  y -= outerRadius;
  return this.path('M'+x+' '+y+'a'+outerRadius+' '+outerRadius +
      ' 0 1 0 1 0m-1 '+
      (outerRadius - innerRadius)+
      'a'+innerRadius+' '+innerRadius+
      ' 0 1 1 -1 0');
};
Eric H.
  • 6,894
  • 8
  • 43
  • 62