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

Répás picture Répás · Mar 26, 2011 · Viewed 9.6k times · Source

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?

Answer

collapsar picture collapsar · Apr 2, 2011

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