Reputation: 45
I'm trying yo make an SVG with a gradient backgound color from red to yellow to green, inside the SVG I've got 2 polygons (for now because there'll be more) that I wish would mask the SVG's background. I tried the following code:
<svg height="500" width="500">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(34,177,76);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,242,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(237,28,36);stop-opacity:1" />
</linearGradient>
</defs>
<polygon points="0, 0, 100, 0, 100, 100, 0, 100" fill="url(#grad1)">
<polygon points="100, 100, 150, 250, 300, 350, 150, 400, 100, 300" fill="url(#grad1)" >
</svg>
How can I apply a background to the SVG and have the polygons show the part of the background based on their position?
UPDATE
I updated my code (YES it needs some work) to achive a path with a unified background and also have transparent polygons in case you wish to add a onclick event.
<?php
//Array with the different sections and their coordinates
$body['item1'] = array(array(0, 0), array(100, 0), array(100, 100), array(0, 100));
$body['item2'] = array(array(100, 100), array(150, 250), array(300, 350), array(150, 300), array(100, 300));
$body['item3'] = array(array(300, 50), array(300, 350), array(200, 350), array(200, 50));
?>
<svg x="0px" y="0px" height="500px" width="500px">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0" style="stop-color:rgb(34,177,76);" />
<stop offset="50%" style="stop-color:rgb(255,242,0);" />
<stop offset="100%" style="stop-color:rgb(237,28,36);" />
</linearGradient>
</defs>
<g id="singlePath_correctGradient" fill="url(#gradient)">
<?php
$polygon = '';
echo '<path d="';
foreach ($body as $key => $values) {
$polygon .= '<polygon points="';
$a = 1;
foreach ($values as $coord) {
echo $a == 1 ? ' M ' . $coord[0] . ', ' . $coord[1] : '';
echo $a == 2 ? ' L ' . $coord[0] . ', ' . $coord[1] : '';
echo $a > 2 ? ', ' . $coord[0] . ', ' . $coord[1] : '';
$polygon .= ($a == 1 ? '' : ', ') . $coord[0] . ', ' . $coord[1];
$a++;
}
$polygon .= '" onclick="alert(\'' . $key . '\')" fill-opacity="0"/>';
}
echo '"></path>';
echo $polygon;
?>
</g>
</svg>
Upvotes: 0
Views: 253
Reputation: 31750
Change your gradientUnits to "userSpaceOnUse" - that way the gradient is defined in the SVG box space, not the filled unit. Easy. (and please, close your elements!! - SVG is XML)
<svg x="0px" y="0px" height="500px" width="500px" viewBox="0 0 500 500">
<defs>
<linearGradient id="grad1" x1="0" y1="0" x2="300" y2="350" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:rgb(34,177,76);" />
<stop offset="50%" style="stop-color:rgb(255,242,0);" />
<stop offset="100%" style="stop-color:rgb(237,28,36);" />
</linearGradient>
</defs>
<polygon points="0, 0, 100, 0, 100, 100, 0, 100" fill="url(#grad1)"/>
<polygon points="100, 100, 150, 250, 300, 350, 150, 400, 100, 300" fill="url(#grad1)" />
</svg>
Upvotes: 1
Reputation: 12458
UPDATE: I now show two snippets below. The first (original) snippet shows a manual solution. The second (newer) snippet shows a programmatic solution using JavaScript.
Are you OK with converting your polygons into paths? If so, you can combine multiple polygons into a single path and then apply the gradient to the whole thing. In the first code snippet below, I show three groups:
Basically, to convert a polygon to a path, change the points
attribute to a d
attribute. Then, within the d
value string, place an M
(for "MoveTo") at the beginning and an L
(for "LineTo") after the first two numbers. Putting an M
halfway through the path is your way of telling the program "lift the pen and, without drawing anything, move it to this new location and continue drawing there", effectively allowing you to draw multiple "shapes" within a single path.
<svg height="500" width="500">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(34,177,76);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,242,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(237,28,36);stop-opacity:1" />
</linearGradient>
</defs>
<g id="twoPolygons_incorrectGradient" fill="url(#grad1)" transform="scale(0.5) translate(0,0)">
<polygon points="0, 0 100, 0, 100, 100, 0, 100"></polygon>
<polygon points="100, 100 150, 250, 300, 350, 150, 400, 100, 300"></polygon>
</g>
<g id="twoPaths_incorrectGradient" fill="url(#grad1)" transform="scale(0.5) translate(300,0)">
<path d="M 0, 0 L 100, 0, 100, 100, 0, 100"></path>
<path d="M 100, 100 L 150, 250, 300, 350, 150, 400, 100, 300"></path>
</g>
<g id="singlePath_correctGradient" fill="url(#grad1)" transform="scale(0.5) translate(600,0)">
<path d="M 0, 0 L 100, 0, 100, 100, 0, 100
M 100, 100 L 150, 250, 300, 350, 150, 400, 100, 300"></path>
</g>
<g>
<text x="0" y="70">wrong</text>
<text x="150" y="70">wrong</text>
<text x="300" y="70">right</text>
</g>
</svg>
I've done the conversions above manually. However, this type of conversion could also be done programmatically, e.g. with JavaScript, which I have done in the second snippet below. Essentially this does the following:
points
attribute value stringd
attribute of the pathThe example below initially shows 7 polygons with the same gradient applied to each individual shape. Note that the last polygon is simply one point and is thus invisible, indicated by the dotted circle.
After clicking the "Combine..." button, polygons 2 and 3 are combined into a single path, as are polygons 4 and 5 as well as polygons 6 and 7. The same gradient as before is now applied to each combined path. Note how the path combinations affect the placement of the colors along the gradient.
Note that the utility function I've written makes it as simple as adding more polygon id's to an array in order to combine more than two polygons together.
var xmlns = "http://www.w3.org/2000/svg";
var combine = function(polygonIds, newPathId) {
var $newPath = $(document.createElementNS(xmlns, "path"));
// jQuery does not work here, e.g. var $newPath = $("<path>", {id: newPathId});
$newPath.attr("id", newPathId);
$("svg").append($newPath);
var dStr = "";
polygonIds.forEach(function(polygonId, idx, arr) {
var $polygon = $("#" + polygonId);
var ptsStr = $polygon.attr("points");
dStr += "M " + ptsStr.replace(/, *| +/g, " ").trim().replace(/^([^ ]+ [^ ]+ )(.*)/, "$1L $2 $1");
});
dStr += "Z";
$newPath.attr("d", dStr);
};
$("button#combine").click(function() {
combine(["shape2", "shape3"], "path2and3");
combine(["shape4", "shape5"], "path4and5");
combine(["shape6", "shape7"], "path6and7");
$("#path2and3").attr("fill", "url(#grad1)");
$("#path4and5").attr("fill", "url(#grad1)");
$("#path6and7").attr("fill", "url(#grad1)");
$("#labels").remove().appendTo($("svg"));
});
$("button#delete").click(function() {
$("path").remove();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Best viewed on "Full Page"</p>
<p>The "Combine..." button will combine polygons 2 and 3, polygons 4 and 5, and polygons 6 and 7. Polygon 7 is a single dot and is thus essentially invisible. The same gradient is applied to each individual polygon as well as to each of the new paths.</p>
<div>
<button id="combine">Combine polygons into paths</button>
<button id="delete" >Delete new paths </button>
</div>
<svg height="500" width="500">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(34,177,76);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,242,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(237,28,36);stop-opacity:1" />
</linearGradient>
</defs>
<g fill="url(#grad1)" transform="scale(1) translate(0,0)">
<polygon id="shape1" points=" 30, 100 60, 160 30, 220 0, 160"></polygon>
<polygon id="shape2" points="100, 0 120, 0 110, 120" ></polygon>
<polygon id="shape3" points="130, 100 160, 160 130, 220 100, 160"></polygon>
<polygon id="shape4" points="230, 100 260, 160 230, 220 200, 160"></polygon>
<polygon id="shape5" points="210, 200 220, 320 200, 320" ></polygon>
<polygon id="shape6" points="330, 100 360, 160 330, 220 300, 160"></polygon>
<polygon id="shape7" points="330, 320" ></polygon>
</g>
<circle cx="330" cy="320" r="10" fill="none" stroke="black" stroke-dasharray="3 3"></circle>
<g id="labels" font-family="Verdana" font-size="18">
<text x=" 24" y="165">1</text>
<text x="104" y=" 20">2</text>
<text x="124" y="165">3</text>
<text x="224" y="165">4</text>
<text x="204" y="310">5</text>
<text x="324" y="165">6</text>
<text x="324" y="305">7</text>
</g>
</svg>
Upvotes: 0