Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to linear-gradient-fill a grouped path in SVG (by css or attr on jQuery event)

How can i fill one gradient for a <g> in an SVG image instead of fill all the <g>s in the selected <g>?

In this case, I'd like to show africa, filled with just one gradient from yellow to red, but because of the sub-groups the fill makes many of gradients.

The javascript:

<script type="text/javascript">
function svgOver() { 
    var what = $(this).attr("id");
    $("#world #"+what, svg.root()).attr("fill", "url(#red_black)"); 
} 
function svgOut() { 
    $(this).attr("fill", "");
}

...

$("#map").svg({ 
    loadURL: 'http://teszt.privilegetours.hu/skins/privilege/svg/worldmap.svg',
        onLoad: function(svg) { 
        $("#world > g", svg.root()).bind('mouseover', svgOver).bind('mouseout', svgOut).bind('click', svgZoom);
        },
    settings: {}
});

The SVG:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" mlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="570px" height="300px" viewBox="146.605 71.42 570 300" enable-background="new 146.605 71.42 570 300" xml:space="preserve">

<defs>
    <linearGradient id="red_black" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1"/>
        <stop offset="100%" style="stop-color:rgb(255,255,0);stop-opacity:1"/>
    </linearGradient>
</defs>

<g id="world" transform="scale(1)" fill="#AAAAAA" stroke="#FFFFFF" stroke-width="0.1">
    <g id="africa" name="africa"> // < i want to fill this
        <g id="er" transform="translate(-29.9017, -45.0745)"> // < instead of theese
            <path d="..."/>
        </g>
        <g id="yt"> // < instead of theese
            <path d="..."/>
        </g> 
        ...

this is the africa

How can I fix this problem?
How can I fix this problem without adding an another <g> tag to the original image?

like image 430
Répás Avatar asked Mar 26 '11 22:03

Répás


3 Answers

your problem can be solved by setting the gradients coordinate system to user space (instead of the default object-bounding box).

you might try

<defs>
    <linearGradient id="red_black" x1="0%" y1="0%" x2="0%" y2="100%" gradientUnits="userSpaceOnUse">
        <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1"/>
        <stop offset="100%" style="stop-color:rgb(255,255,0);stop-opacity:1"/>
    </linearGradient>
</defs>

the solution does not defy the comment by e.nelson - what happens here is that each subgroup representing the nations still has its individual gradient instance applied while all these instances share the same coord origin and the same transformations w.r.t the user space - so at any point in the final rendering, it doesn't matter which gradient instance is visible.

two adjustments are required:

  1. [ minor] you have to adjust the y1/y2 offsets (or the stop offsets) of the gradient definition - as they refer to the user coord space of the whole map, africa only covers a portion of the gradient between the stops defined. try y1="50%" and y2="100%".

  2. [medium] if you have a look at svg g-elements defining the country shapes you'll note that some of them are subjected to an additional translation. they effectively shift the user coordinate system and therefore also apply to the gradient which causes the affected country shapes to appear like blotches on the map. this spurios transform is probably an artefact of the actions in the generator used to create the map. it can be remedied by adding the translation offsets to each absolute coordinate in the path elements inside the respective g-elements. as these paths are defined using relative coordinates for the pieces stitched together, this reduces to altering the coords of the initial 'M' and final 'C' commands in the path's d-attribute.

i have cooked up an ad hoc perl script to normalize the structure of the svg code representing the country borders that implements the abovementioned modifications. note that these alterations can be done rather conveniently in js too. This is the result.

hope that helps and drop me a note if you need additional informations on how to perform the adjustments mentioned.

PS: i just noticed that mozambique is still missing from the generated output - for that single country's shape yet another translation has been specified. this minor detail is something to be added later today, however ...

result

like image 66
collapsar Avatar answered Oct 16 '22 02:10

collapsar


"Painting, however, is always done on each graphics element individually, never at the container element (e.g., a 'g') level. Thus, for the following SVG, even though the gradient fill is specified on the 'g', the gradient is simply inherited through the 'g' element down into each rectangle, each of which is rendered such that its interior is painted with the gradient."

http://www.w3.org/TR/SVGTiny12/painting.html#InheritanceOfPaintingProperties

What you're asking for isn't possible, according to the spec. If it's a requirement, you could explore one of: having the SVG creator add mouse over paths for you; combine the paths in code on the server (potentially tricky); choose a solid color instead of a gradient so the problem isn't so obvious.

like image 2
Elliot Nelson Avatar answered Oct 16 '22 03:10

Elliot Nelson


If you want to fill all of Africa with one gradient, then you want the union the paths for that fill. Maybe you should use a different map? One with only the continents?

Anyway one way to fix it would be to:

  1. open it up in Inkscape
  2. select all the paths that you want to fill
  3. choose "Union" from the "Path" menu
  4. save the file (or copy&paste the unioned path)

Another way:

  1. Look for another map, see http://d-maps.com/ or http://commons.wikimedia.org. Here's one with the continents only, africa marked.

After doing that you can apply the gradient to that new path.

You could also do it in some other ways, but they're probably not as good for performance reasons. One of those (not recommended) ways would be to fill a rectangle with the gradient where you've made a clip-path consisting of the paths in the group. Something along these lines:

<clipPath id="clip">
  <use xlink:href="#africa"/>
</clipPath>
<rect width="100" height="100" fill="url(#grad)" clip-path="url(#clip)"/>
<g id="africa">...</g>
like image 2
Erik Dahlström Avatar answered Oct 16 '22 01:10

Erik Dahlström