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>
Related
I am trying to figure out conversion from gradienttransform to gradientunits. I am getting different gradient distribution. Why?
<svg width="540" height="540" xmlns="http://www.w3.org/2000/svg" >
<defs>
<linearGradient id="linear1" gradientTransform="rotate(45 0.5 0.5)" spreadMethod="pad">
<stop offset="0%" stop-color="gold"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
<linearGradient id="linear2" x1="0%" y1="0%" x2="100%" y2="100%" spreadMethod="pad">
<stop offset="0%" stop-color="gold"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
</defs>
<rect fill="url(#linear1)" x="0" y="0" width="270" height="270" />
<rect fill="url(#linear2)" x="0" y="270" width="270" height="270" />
</svg>
These 2 gradients are not the same:
because the distance between the left side and right side of a rectangle does not equal the distance from one corner to the opposite corner when you rotate it 45 degrees.
the left and right sides are 270 units apart (default x2 is 100% the others default to 0%).
the corners 270 * √2 units apart
So the distance between x1,y1 and x2,y2 is different in each case given that the rotation transform is distance invariant.
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.
I have two svg animations and I want to start #anim-join-gradient animation on the end of #anim-vert-gradient, but #anim-join-gradient animation doesn't start.
#anim-join-gradient element has attribute begin="anim-vert-gradient.end"
.box1{width:25px}
<div class="box box1">
<svg viewbox="0 0 100 100">
<path id="button" class="arrow" d="M 50,0 L 60,10 L 20,50 L 60,90 L 50,100 L 0,50 Z" />
</svg>
</div>
<svg id="play-vert-line" width="110" height="110">
<defs>
<linearGradient id="vert-gradient" gradientUnits="userSpaceOnUse" y1="0%" y2="100%" x1="0" x2="0">
<stop stop-color="#1689fb"></stop>
<stop stop-color="#7e7e7e"></stop>
<animate xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#vert-gradient" id="anim-vert-gradient" attributeName="y1" dur="800ms" to="99%" begin="button.click" fill="freeze"></animate>
</linearGradient>
</defs>
<line id="vert-line-1" x1="55.3" y1="0" x2="55.3" y2="105" stroke="url(#vert-gradient)" stroke-width="2"></line>
</svg>
<svg id="line-join" width="110" height="110">
<defs>
<linearGradient id="vert-join-gradient" gradientUnits="userSpaceOnUse" y1="0%" y2="100%" x1="0" x2="0">
<stop stop-color="#1689fb"></stop>
<stop stop-color="#7e7e7e"></stop>
<animate xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#vert-join-gradient" id="anim-join-gradient" attributeName="y1" dur="400ms" from="0%" to="99%" begin="anim-vert-gradient.end" fill="freeze"></animate>
</linearGradient>
</defs>
<line id="vert-line-2" x1="105" y1="0" x2="105" y2="105" stroke="url(#vert-join-gradient)" stroke-width="2"></line>
</svg>
minus signs are treated specially in begin attributes. Unescaped minus signs are treated as the subtraction operator. Per the spec you need to escape them if you don't want that e.g.
begin="anim\-vert\-gradient.end"
That would work on Firefox. Chrome has a bug and doesn't support escaping so if you want it to work there you'd have to remove the - signs.
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>
I'm trying to generate a parallelogram in SVG with repeating lines that are along the same angle as the left and right sides of the polygon. Something like this:
I got the repeating gradient to work, but I can't get the angle of the lines right. They're skewed from the angle of the bounding parallelogram:
I know I can manipulate the angle based on the (x1, y1) / (x2, y2) attributes of the gradient, but just playing with the numbers isn't doing it for me. How can I calculate which values to use for these attributes given a known angle?
Here's the SVG code I have right now:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1680 550" preserveAspectRatio="xMidYMin">
<defs>
<linearGradient id="StripedGradient" spreadMethod="repeat" x1="0%" x2="1%" y1="0%" y2="1%">
<stop stop-color="yellow" offset="0%" />
<stop stop-color="yellow" offset="15%" />
<stop stop-color="transparent" offset="15%" />
<stop stop-color="transparent" offset="100%" />
</linearGradient>
</defs>
<polygon fill="url(#StripedGradient)" points="0 550, 415 145, 610 145, 195 550" />
</svg>
This angle currently works out to be just under 45deg (44.301...), but the value could change at the discretion of the designer, so...
(I'm really new to SVG, as in I knew it existed, but I had never written inline SVG by hand until today.)
One fairly simple approach would be to define your gradient as a horizontal stripe. Then if you also use gradientUnits="userSpaceOnUse", you can use a gradientTransform to set your angle.
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 750 550" preserveAspectRatio="xMidYMin">
<defs>
<linearGradient id="StripedGradient" spreadMethod="repeat"
gradientUnits="userSpaceOnUse"
x1="0" y1="0" x2="0" y2="20"
gradientTransform="rotate(-44.301)">
<stop stop-color="red" offset="0%" />
<stop stop-color="red" offset="15%" />
<stop stop-color="transparent" offset="15%" />
<stop stop-color="transparent" offset="100%" />
</linearGradient>
</defs>
<polygon fill="url(#StripedGradient)" points="0 550, 415 145, 610 145, 195 550" />
</svg>