Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best approach for overlapping SVG elements area fill?

Tags:

javascript

svg

I am studying some basic image manipulations with SVG and trying to find optimal approach for the following challenge:

We have one SVG file which has various SVG elements (circles, rectangle, triangle). They all are overlapping each other creating new "areas" of different forms (see pic).

example of areas

So filling actual Elements - no problem there. But what if I want to fill with color only specific intersect area?

My current thinking was:

  • Consider drawing all elements as Paths, then see if I can treat overall composition as One large path and then play with fill-rule.
  • Consider calculating the area shape and drawing a new shape on top of it, then fill it.
  • Consider something else?
like image 352
Sergey Rudenko Avatar asked Aug 03 '16 19:08

Sergey Rudenko


2 Answers

Michael's filter method is cool and tricky, but perhaps a little hard to understand.

You can also do it with masks.

<svg width="391" height="400">
  <defs>
    <!-- define the shapes in the image, which we will use for the outlines
         and for creating intersection masks -->
    <rect id="square" x="92" y="48" width="218" height="218"/>
    <polygon id="triangle" points="54,366 277,366 165,142"/>
    <circle id="circle" cx="256" cy="264" r="85"/>
    
    <!-- the masks -->
    <!-- white parts are visible, black parts are invisible -->
    <mask id="square-minus-triangle">
      <!-- square with triangle cut out of it -->
      <use xlink:href="#square" fill="white"/>
      <use xlink:href="#triangle" fill="black"/>
    </mask>
    <mask id="triangle-minus-square">
      <!-- triangle with square cut out of it -->
      <use xlink:href="#triangle" fill="white"/>
      <use xlink:href="#square" fill="black"/>
    </mask>
  </defs>
  
  <!-- background -->
  <rect width="100%" height="100%" fill="#e5e4da"/>
  
  <!-- the intersection shapes (yellow) -->
  <!-- first draw the circle, but use the square-minus-triangle mask.-->
  <use xlink:href="#circle" fill="#e4e400" mask="url(#square-minus-triangle)"/>
  <!-- draw the circle again, but use the triangle-minus-square mask.-->
  <use xlink:href="#circle" fill="#e4e400" mask="url(#triangle-minus-square)"/>
  
  <!-- draw the outlined shapes -->
  <g fill="none" stroke="black" stroke-width="6">
    <use xlink:href="#square"/>
    <use xlink:href="#triangle"/>
    <use xlink:href="#circle"/>
  </g>
</svg>
like image 152
Paul LeBeau Avatar answered Nov 15 '22 04:11

Paul LeBeau


You can do this with filters. An easy way to do is to use near transparent fill and then use a filter to dial the non-overlapping areas to fully transparent and the overlapping areas to fully opaque. It makes the stroke a little crispy though.

<svg height="600px" width="800px">
     <defs>
       <filter id="opacitychange">
         <feComponentTransfer>
           <feFuncA type="linear" intercept="-.05"/>
           </feComponentTransfer>
         <feComponentTransfer>
           <feFuncA type="gamma" amplitude="4" exponent=".4"/>
           </feComponentTransfer>
         </filter>
       </defs>
  
  <g filter="url(#opacitychange)">
     <circle stroke="black"  fill="blue" fill-opacity="0.05" cx="150" cy="150" r="100"/>
     <rect stroke="black" x="200" y="100" width="100" height="300" fill="blue" fill-opacity="0.05"/>
     <polygon stroke="black"  points="50,50 50,400 300,400" fill="blue" fill-opacity="0.05"/>
 </g>    
     </svg>
like image 45
Michael Mullany Avatar answered Nov 15 '22 03:11

Michael Mullany