Как достичь "отверстий пончика" с путями в Рафаэле

Я хотел бы нарисовать фигуру с отверстиями в ней, чтобы я мог fill сформировать ее и не иметь отверстий, заполненных этим цветом (оставьте их прозрачными).

В соответствии с спецификацией пути W3:

Сложные пути (т.е. путь с несколькими подпутами) позволяют разрешать такие эффекты, как "отверстия пончика" в объектах.

Может кто-нибудь, пожалуйста, дайте очень простой пример того, как выполнить это с векторным путем в Raphael?

Большое спасибо.

Ответ 1

Это оказывается довольно простым, если вы знаете трюк. Например это не работает:

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");

Но это работает *:

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");

Разница в том, что для появления пончика внутренний путь должен иметь вершины, обращенные в обратном порядке к внешнему пути (т.е. нарисовать один по часовой стрелке, другой против часовой стрелки). Я нашел в text.xml.svg.devel архивах.

(*) По крайней мере, он работает в бета-версии Chrome, Opera и Firefox 4.0, но не в версии 3.6

Ответ 2

Чтобы сделать эту работу в Firefox 3.6, вам нужно закрыть отверстие; т.е. заставить координаты присоединяться к себе при определении внутренней границы. Любопытно, что это не представляется необходимым для внешней границы.

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");

Просто краткое примечание, чтобы следить за комментарием - концепция по часовой стрелке/против часовой стрелки может показаться странной вначале, но она довольно стандартная по всем технологиям ГИС/САПР.

Ответ 3

Я думаю, что правильный способ сделать это - установить атрибут "fill-rule" в значение "evenodd". Взгляните на svg spec:

Не пытайтесь установить его с помощью "Raphael.Element.attr()". Это не работает. Вместо этого я использую функцию jQuery.attr():

// 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 done
$(path[0]).attr('fill-rule', 'evenodd');

Я использовал это на сложных путях с успешными результатами.

Ответ 4

Для тех, кто хочет делать круговые пончики, отличный плагин Raphael-donut-plugin

Суть:

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');
};