I needed to add a radial / conical gradient into an SVG circle. Unfortunately, there is no support for that on the standard: https://wiki.inkscape.org/wiki/index.php/Advanced_Gradients#Conical_gradient
So, I went for a solution similar to this: https://stackoverflow.com/a/18210763 but instead of using multiple colors, I use the same color with an alpha variation.
Mi final result ended like this:
<g fill="none" stroke-width="8" transform="translate(-49,-10) scale(1.2) rotate(0, 125, 50)" opacity="1">
<path d="M127.5593032836914,78.30342873930931 C124.73236846923828,78.30342873930931 122.4406967163086,76.50826272368431 122.4406967163086,74.29380044341087 C122.4406967163086,72.07933816313744 124.73236846923828,70.28415688872337 127.5593032836914,70.28415688872337 V78.30342873930931 z" fill="#23B26D"
transform="scale(-1,1) translate(-252,-48.7)" />
<path d="M124.99998474121094,25.63152313232422 A24.357080459594727,24.368465423583984 0 0 1 146.0932159423828,37.81575393676758 "
stroke="url(#redyel)" />
<path d="M146.0932159423828,37.81575393676758 A24.357080459594727,24.368465423583984 0 0 1 146.0932159423828,62.18423271179199 "
stroke="url(#yelgre)" />
<path d="M146.0932159423828,62.18423271179199 A24.357080459594727,24.368465423583984 0 0 1 124.99998474121094,74.36847496032715 "
stroke="url(#grecya)" />
<path d="M124.99998474121094,74.36847496032715 A24.357080459594727,24.368465423583984 0 0 1 103.90676879882812,62.18423271179199 "
stroke="url(#cyablu)" />
<path d="M103.90676879882812,62.18423271179199 A24.357080459594727,24.368465423583984 0 0 1 103.90676879882812,37.81575393676758 "
stroke="url(#blumag)" />
<path d="M103.90676879882812,37.81575393676758 A24.357080459594727,24.368465423583984 0 0 1 124.99998474121094,25.63152313232422 "
stroke="url(#magred)" />
</g>
<defs>
<linearGradient id="redyel" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="0.41"/>
</linearGradient>
<linearGradient id="yelgre" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="0"
y2="1">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.41"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="0.52"/>
</linearGradient>
<linearGradient id="grecya" gradientUnits="objectBoundingBox" x1="1" y1="0" x2="0"
y2="1">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.52"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="0.63"/>
</linearGradient>
<linearGradient id="cyablu" gradientUnits="objectBoundingBox" x1="1" y1="1" x2="0"
y2="0">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.63"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="0.74"/>
</linearGradient>
<linearGradient id="blumag" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="0"
y2="0">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.74"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="0.85"/>
</linearGradient>
<linearGradient id="magred" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="1"
y2="0">
<stop offset="0%" stop-color="#23B26D" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#23B26D" stop-opacity="1"/>
</linearGradient>
<linearGradient id="lgrad" x1="100%" y1="50%" x2="0%" y2="50%">
<stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:0"/>
<stop offset="60%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
</linearGradient>
<linearGradient id="rgrad" x1="0%" y1="50%" x2="100%" y2="50%">
<stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:0"/>
<stop offset="60%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255,255,255);stop-opacity:1"/>
</linearGradient>
</defs>
https://codepen.io/scvsoft-marianovicente/pen/abmrdXo
It looks good enough, but now, I have small spaces between each element. (Depending on the browser in some places looks more visible than others).
I tried to apply a distortion/blur. to hide that but affects all borders.
It is possible to hide this spaces?
Try an annealing filter (dilate/erode). See if it solves your issues.
<svg class="streak-animation" viewbox="0 0 250 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="anneal">
<feMorphology operator="dilate" radius="1" />
<feMorphology operator="erode" radius="1" />
</filter>
</defs>
<g class="all-streaks" filter="url(#anneal)">
More details - as requested:
The feMorphology filter replaces every pixel's R,G,B and A values with the maximum (dilate) or minimum (erode) value of those color channels in a 2*radius. For simple monochrome shapes, this effectively adds (or subtracts) a one pixel outline around the shape.
In this case, the dilate fills in the super thin lines between shapes (and adds a 1px border around the whole shape). But then when you do an erode, there is no longer any semi-transparent pixels between the shapes to supply a low alpha value to erode, so only the 1px outline around the whole shape is removed.
Related
I have this radial gradient expressed in objectBoundingBox coordinates.
<svg width="300" height="300">
<defs>
<radialGradient id="MyGradient" gradientUnits="objectBoundingBox"
cx="0.3" cy="0.4" r="0.3" fx="0.1" fy="0.2">
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="black" />
</radialGradient>
</defs>
<rect fill="url(#MyGradient)" stroke="black" stroke-width="5"
x="50" y="100" width="200" height="100"/>
</svg>
Is it possible to convert it to userSpaceOnUse coordinates?
For this question it is same to assume that each radial gradient only applies to one shape, and that we know x, y, width, and height of said shape.
<svg width="300" height="300">
<defs>
<radialGradient id="MyGradient" gradientUnits="userSpaceOnUse"
cx="?" cy="?" r="?" fx="?" fy="?">
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="black" />
</radialGradient>
</defs>
<rect fill="url(#MyGradient)" stroke="black" stroke-width="5"
x="50" y="100" width="200" height="100"/>
</svg>
First I would show how to do it if the rect you fill with the gradient is a square:
svg{border:solid; width:45vw}
<svg viewBox="0 0 300 300">
<defs>
<radialGradient id="MyGradient" gradientUnits="objectBoundingBox"
cx="0.3" cy="0.4" r="0.3" fx="0.1" fy="0.2">
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="black" />
</radialGradient>
</defs>
<rect fill="url(#MyGradient)" stroke="black" stroke-width="5"
x="50" y="100" width="200" height="200"/>
</svg>
<svg viewBox="0 0 300 300">
<defs>
<radialGradient id="MyGradient1" gradientUnits="userSpaceOnUse"
cx="110" cy="180" r="60" fx="70" fy="140" >
<stop offset="0%" stop-color="red" />
<stop offset="50%" stop-color="blue" />
<stop offset="100%" stop-color="black" />
</radialGradient>
</defs>
<rect fill="url(#MyGradient1)" stroke="black" stroke-width="5"
x="50" y="100" width="200" height="200"/>
</svg>
In the case of `gradientUnits="userSpaceOnUse":
The bounding box of the rect to fill is:
bb:{x:50, y:100, width:200, height:200}
The calculated attributes for gradientUnits="userSpaceOnUse" are:
cx = bb.x + bb.width *.3 = 50 + 200 * .3 = 110
cy = bb.y + bb.height *.4 100 + 200*.4 = 180
r = bb.width*.3 = 200*.3 = 60
fx = bb.x + bb.width *.1 = 50 + 200 * .1 = 70
fy = bb.y + bb.height *.2 = 100 + 200 * .2 = 140
So you can use
<radialGradient id="MyGradient1" gradientUnits="userSpaceOnUse" cx="110" cy="180" r="60" fx="70" fy="140" >
When using objectBoundingBox the values of the attributes of the radialGradient are taking values between 0 and 1 or 0 and 100% of the filled box.
You can also use objectBoundingBox as a value for clipPathUnits. Please take a look at the folowing example.
There is this clipPath where the clipping path is a circle. If the clipped shape is a square the result is a circle. If the clipped shape is a rectangle the result is an ellipse meaning that the clipping path is stretched according to the aspect ratio of the clipped shape.
<svg viewBox="0 0 120 60">
<defs>
<clipPath id="clip" clipPathUnits="objectBoundingBox">
<circle cx=".5" cy=".5" r=".45" />
</clipPath>
</defs>
<rect id="r" x="5" y="5" width="50" height="50" />
<use xlink:href="#r" fill="gold" clip-path="url(#clip)" />
<rect id="r1" x="60" y="15" width="55" height="30" />
<use xlink:href="#r1" fill="gold" clip-path="url(#clip)" />
</svg>
The same is happening with the gradient. If the radial gradient with gradientUnits="objectBoundingBox" is used to fill a rectangle with a different width and height the result would be an elliptical gradient (as in your example). If you want to translate an elliptical gradient to gradientUnits="userSpaceOnUse" you would need a way to create a gradient with a different rx and ry. Unfortunately this is not posible.
How can I draw a circle in SVG with 2 colors?
I found a tutorial the following example (from this JSFiddle):
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="-10 -10 220 220">
<defs>
<linearGradient id="redyel" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#ff0000"/>
<stop offset="100%" stop-color="#ffff00"/>
</linearGradient>
<linearGradient id="yelgre" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ffff00"/>
<stop offset="100%" stop-color="#00ff00"/>
</linearGradient>
<linearGradient id="grecya" gradientUnits="objectBoundingBox" x1="1" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#00ff00"/>
<stop offset="100%" stop-color="#00ffff"/>
</linearGradient>
<linearGradient id="cyablu" gradientUnits="objectBoundingBox" x1="1" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#00ffff"/>
<stop offset="100%" stop-color="#0000ff"/>
</linearGradient>
<linearGradient id="blumag" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#0000ff"/>
<stop offset="100%" stop-color="#ff00ff"/>
</linearGradient>
<linearGradient id="magred" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="1" y2="0">
<stop offset="0%" stop-color="#ff00ff"/>
<stop offset="100%" stop-color="#ff0000"/>
</linearGradient>
</defs>
<g fill="none" stroke-width="15" transform="translate(100,100)">
<path d="M 0,-100 A 100,100 0 0,1 86.6,-50" stroke="url(#redyel)"/>
<path d="M 86.6,-50 A 100,100 0 0,1 86.6,50" stroke="url(#yelgre)"/>
<path d="M 86.6,50 A 100,100 0 0,1 0,100" stroke="url(#grecya)"/>
<path d="M 0,100 A 100,100 0 0,1 -86.6,50" stroke="url(#cyablu)"/>
<path d="M -86.6,50 A 100,100 0 0,1 -86.6,-50" stroke="url(#blumag)"/>
<path d="M -86.6,-50 A 100,100 0 0,1 0,-100" stroke="url(#magred)"/>
</g>
</svg>
But I want to only use 2 colors. I'm trying to make it look like this:
Thanks for helping!
You can actually create a circle with two colours with only a single path. Simply set two different stop-color. Here, the first stop-color is #00f (blue), and the secnd stop-color is #f0f (purple):
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="-10 -10 220 220">
<defs>
<linearGradient id="colour" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="1" y2="1">
<stop offset="0%" stop-color="#00f"/>
<stop offset="100%" stop-color="#f0f"/>
</linearGradient>
</defs>
<g fill="none" stroke-width="15" transform="translate(100,120)">
<path d="M 0 -100 a 50 50 0 1 0 0.00001 0" stroke="url(#colour)"/>
</g>
</svg>
I've also created a JSFiddle of this here.
Feel free to play around with the viewBox and transform to suit the desired output location :)
Hope this helps!
I am wondering how to make linearGradient across (from top to bottom) the line, as opposed to the example below where the gradient goes along (from left to right) the line.
<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="40" y1="210" x2="460" y2="210" gradientUnits="userSpaceOnUse">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30" />
</svg>
Changing y coordinates works nicely for an un-rotated line and linearGradient now goes across (from top to bottom) the line:
<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="40" y1="195" x2="40" y2="225" gradientUnits="userSpaceOnUse">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30"/>
</svg>
But this doesn't work when rotated:
<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="40" y1="235" x2="40" y2="265" gradientUnits="userSpaceOnUse">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/>
</svg>
And what I want to have is the rotated line with linear gradient across it.
Something like this:
<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="40" y1="210" x2="460" y2="290" gradientUnits="userSpaceOnUse">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/>
</svg>
The trick to your first case is to make the x1 y1,x2 y2 of the line match the x1 y1,x2 y2 coordinates of the linear gradient.
For the second case it is a little more involved Math-wise.
You have to create a line that is perpendicular to the first and has the length of the width of the desired line and also start at half the width from one of the points.
So in your case (in pseudocode!):
Step 1:
get the direction
dx=x2-x1;
dy=y2-y1;
dx,dy is now the direction from point 1 to point2
Step 2:
normalise the direction to length 1 by dividing dx and dy by the length of the line.
len=Math.sqrt(dx*dx+dy*dy);
dx=dx/len;
dy=dy/len;
Ofcourse this doesn't work if len=0, but because you gave me coords I don't have to worry about that now.
Step 3:
Find the perpendicular direction. This is actually very easy, but logically can be two directions. I'll just choose one.
temp=dx;
dx=-dy;
dy=temp;
If you want the other direction, just negate dx and dy. after this process.
dx=-dx;
dy=-dy;
dx, dy now holds the perpendicular direction.
Step 4:
multiply dx and dy by the desired width of the line, in your case 30. I have called this w.
dx=w*dx;
dy=w*dy;
Step 5:
To find the p1 and p2 for the gradient, take p1 from the line and add or subtract half the dx.
gradient_x1=x1+dx*0.5;
gradient_y1=y1+dx*0.5;
gradient_x2=x1-dx*0.5;
gradient_y2=y1-dx*0.5;
Now you can build your line up again.
To show you what I mean, I've plugged in your values and done the whole thing and I get this:
Your case: (x1="40" y1="210" x2="460" y2="290" w=30)
## STEP1 ##
dx: 420 dy:80
## STEP2 ##
dx: 0.9823385664224747 dy:0.1871121078899952
## STEP3 ##
dx: -0.1871121078899952 dy:0.9823385664224747
## STEP4 ##
dx: -5.613363236699856 dy:29.47015699267424
## STEP5 ##
gradient_x1=37.19331838165007
gradient_y1=224.7350784963371
gradient_x2=42.80668161834993
gradient_y2=195.2649215036629
so plug-in that in to your example:
<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="37.19331838165007" y1="224.7350784963371" x2="42.80668161834993" y2="195.2649215036629" gradientUnits="userSpaceOnUse">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/>
</svg>
Wrapping up
Luckily, we don't have to do all this calculating at all, since we have a computer and svg elements can be easily manipulated by javascript.
To get to the elements in svg with javascript it's most handy if they have an id.
Your gradient has an id="e", let's give your line and id="l".
After that it's a question of inserting a little script into the page
to take the x1 y1,x2 y2 from the line ("l") and calculating everything and put it in the gradient ("e"), and you get this:
<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="0" y1="0" x2="1" y2="1" gradientUnits="userSpaceOnUse">
<!-- put the coords on 0,0 1,1 it really doesn't matter, they will be calculated-->
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line id="l" x1="40" y1="270" x2="450" y2="210" stroke="url(#e)" stroke-width="30"/>
</svg>
<script>
var line=document.getElementById("l");
var x1=parseFloat(l.getAttribute("x1"));
var y1=parseFloat(l.getAttribute("y1"));
var x2=parseFloat(l.getAttribute("x2"));
var y2=parseFloat(l.getAttribute("y2"));
var w=parseFloat(l.getAttribute("stroke-width"));
// step 1
var dx=x2-x1;
var dy=y2-y1;
// step 2
len=Math.sqrt(dx*dx+dy*dy);
dx=dx/len;
dy=dy/len;
// step 3
var temp=dx;
dx=-dy;
dy=temp;
//step 4
dx=w*dx;
dy=w*dy;
//step 5
var gradient_x1=x1+dx*0.5;
var gradient_y1=y1+dy*0.5;
var gradient_x2=x1-dx*0.5;
var gradient_y2=y1-dy*0.5;
document.getElementById("e");
e.setAttribute("x1",gradient_x1);
e.setAttribute("y1",gradient_y1);
e.setAttribute("x2",gradient_x2);
e.setAttribute("y2",gradient_y2);
</script>
You can freely edit the beginning and endpoints of the line and even the stroke-width, the script will fix your gradient on the fly. To 'prove' this to you, that is exactly what I did. :)
Hope this helps.
Do you mean rotate the gradient? Then use gradientTransform
<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
<linearGradient id="e" x1="40" y1="210" x2="460" y2="210" gradientUnits="userSpaceOnUse" gradientTransform="rotate(90)">
<stop stop-color="steelblue" offset="0" />
<stop stop-color="red" offset="1" />
</linearGradient>
</defs>
<line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30" />
</svg>
I am having trouble making SVG vertical linear gradients work in IE 11
, so the question is - given the following svg how to make the gradient identified as Gradient1 work in such a way that it is a vertical gradient?
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 79.4 59.5" xml:space="preserve">
<rect x="2.4" y="29.8" width="74.6" height="27.4"/>
<g>
<defs>
<linearGradient id="Gradient1" gradientUnits="userSpaceOnUse" x1="100%" y1="0%" x2="100%" y2="100%" gradientTransform="rotate(45 50 50)">
<stop offset="0%" style="stop-color:#9AAFCC"/>
<stop offset="20%" style="stop-color:#557096"/>
<stop offset="35%" style="stop-color:#36557D"/>
<stop offset="49%" style="stop-color:#1E3F6B"/>
<stop offset="63%" style="stop-color:#0D305D"/>
<stop offset="87%" style="stop-color:#032756"/>
<stop offset="100%" style="stop-color:#002453"/>
</linearGradient>
</defs>
<rect x="2.4" y="2.4" class="st0" width="74.6" height="27.4" fill="url(#Gradient1)"/>
</g>
</svg>
I have of course tried to use a gradientTransform, what I have that works in Chrome (and presumably other browsers but not in IE) includes the following on the linearGradient element
x1="-383.9706" y1="317.1023" x2="-382.9706" y2="317.1023" gradientTransform="matrix(0 -27.3826 -27.3826 0 8722.7842 -10484.3682)"
however as soon as I try to do this in IE the gradient I have stops working and the rectangle just takes the first full stop color.
I am open to translating the gradient into CSS if it can be made to work in the svg if that is the only cross-browser solution available.
Your example works the same in Chrome and IE for me.
<svg viewBox="0 0 79.4 59.5">
<rect x="2.4" y="29.8" width="74.6" height="27.4"/>
<defs>
<linearGradient id="Gradient1" gradientUnits="userSpaceOnUse" x1="100%" y1="0%" x2="100%" y2="100%" gradientTransform="rotate(45 50 50)">
<stop offset="0%" style="stop-color:#9AAFCC"/>
<stop offset="20%" style="stop-color:#557096"/>
<stop offset="35%" style="stop-color:#36557D"/>
<stop offset="49%" style="stop-color:#1E3F6B"/>
<stop offset="63%" style="stop-color:#0D305D"/>
<stop offset="87%" style="stop-color:#032756"/>
<stop offset="100%" style="stop-color:#002453"/>
</linearGradient>
</defs>
<rect x="2.4" y="2.4" class="st0" width="74.6" height="27.4" fill="url(#Gradient1)"/>
</svg>
It's a linear gradient rotated 45 degrees as your markup requests.
If you want the gradient to be vertical, remove the gradientTransform.
<svg viewBox="0 0 79.4 59.5">
<rect x="2.4" y="29.8" width="74.6" height="27.4"/>
<defs>
<linearGradient id="Gradient1" gradientUnits="userSpaceOnUse" x1="100%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#9AAFCC"/>
<stop offset="20%" style="stop-color:#557096"/>
<stop offset="35%" style="stop-color:#36557D"/>
<stop offset="49%" style="stop-color:#1E3F6B"/>
<stop offset="63%" style="stop-color:#0D305D"/>
<stop offset="87%" style="stop-color:#032756"/>
<stop offset="100%" style="stop-color:#002453"/>
</linearGradient>
</defs>
<rect x="2.4" y="2.4" class="st0" width="74.6" height="27.4" fill="url(#Gradient1)"/>
</svg>
(source: elv1s.ru)
What a proper way to do a gradient like this? I tried this SVG:
<linearGradient id="hue" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#ff0000"/>
<stop offset="17%" stop-color="#ffff00"/>
<stop offset="34%" stop-color="#00ff00"/>
<stop offset="50%" stop-color="#00ffff"/>
<stop offset="66%" stop-color="#0000ff"/>
<stop offset="82%" stop-color="#ff00ff"/>
<stop offset="100%" stop-color="#ff0000"/>
</linearGradient>
It works, but still not good enough:
(source: elv1s.ru)
I can see the difference between those two gradients. Is there a better way of doing it?
Have you tried using better precision in your %? As Josh is showing, you can have sub-percent precision and space your stops better.
If it's acceptable to generate the SVG with Javascript you might want to look at: How to interpolate hue values in HSV colour space?
or perhaps:
http://www.carto.net/papers/svg/gui/colourPicker/
Edit: Actually there is one here. would that be correct?
http://upload.wikimedia.org/wikipedia/commons/5/5d/HSV-RGB-comparison.svg
<linearGradient
id="linearGradient3706">
<stop
style="stop-color:#ff1c1c;stop-opacity:1;"
offset="0"
id="stop3708" />
<stop
id="stop3728"
offset="0.16105497"
style="stop-color:#fd00ca;stop-opacity:1;" />
<stop
id="stop3724"
offset="0.35173747"
style="stop-color:#0202ff;stop-opacity:1;" />
<stop
id="stop3720"
offset="0.48789391"
style="stop-color:#02fff9;stop-opacity:1;" />
<stop
id="stop3718"
offset="0.70091939"
style="stop-color:#60ff18;stop-opacity:1;" />
<stop
id="stop3714"
offset="0.83720928"
style="stop-color:#ffef15;stop-opacity:1;" />
<stop
style="stop-color:#ff0000;stop-opacity:1;"
offset="1"
id="stop3710" />
</linearGradient>
works for me (made with inskape)