JavaScript based SVG gradient manipulation on IE9 - javascript
I have an SVG file which specifies a gradient and a circle like below. The embedded script toggles the orientation of the gradient onClick(), which works in all current browsers except IE9. My suspicion is that IE does not redraw the gradient. I tried a few things, such as setting the fill to a solid colour first and thenm reassign the (altered) gradient, to trigger a redraw but no dice so far. My question is, does anybody how I can work around that issue or, even better, solve it. Thanks.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" height="70" width="190">
<script type="text/javascript">
<![CDATA[
function flipGrad(evt) {
var g=document.getElementById('grad1');
var y1 = g.getAttribute('y1');
var y2 = g.getAttribute('y2');
g.setAttribute('y2', y1);
g.setAttribute('y1', y2);
}
]]>
</script>
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
</defs>
<circle cx="30" cy="30" r="20" stroke="black" stroke-width="2" fill="url(#grad1)" onclick="flipGrad(evt)" />
</svg>
EDIT:
Editing the stops help, so that might become workable. Truth is though, my actual file looks more like the following, which is Inkscape svg output, where gradients are split into colour sections and geometry sections and only the geometry is linked to from and object but the colour is reused in multiple other gradients. The approach to swap the stops would effect all objects linking to that color gradient:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" height="70" width="190">
<script type="text/javascript">
<![CDATA[
function flipGrad(evt) {
// var g=document.getElementById('gradGeometry');
// var y1 = g.getAttribute('y1');
// var y2 = g.getAttribute('y2');
// g.setAttribute('y2', y1);
// g.setAttribute('y1', y2);\
var s1=document.getElementById('stop1');
var s2=document.getElementById('stop2');
var s1s = s1.getAttribute('style');
var s2s = s2.getAttribute('style');
s1.setAttribute('style', s2s);
s2.setAttribute('style', s1s);
}
]]>
</script>
<defs>
<linearGradient id="gradColour">
<stop id="stop1" offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop id="stop2" offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="gradGeometry1" x1="0%" y1="0%" x2="0%" y2="100%" xlink:href="#gradColour" />
<linearGradient id="gradGeometry2" x1="0%" y1="0%" x2="100%" y2="0%" xlink:href="#gradColour" />
</defs>
<circle cx="30" cy="30" r="20" stroke="black" stroke-width="2" fill="url(#gradGeometry1)" onclick="flipGrad(evt)" />
<circle cx="90" cy="30" r="20" stroke="black" stroke-width="2" fill="url(#gradGeometry2)" onclick="flipGrad(evt)" />
</svg>
After testing a bit on IE9, it seems that the gradient vector is immutable once defined in IE. Your only choice is to use id'd gradient stops, which are mutable.
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" height="70" width="190">
<script type="text/javascript">
<![CDATA[
function flipGrad(evt) {
var s1=document.getElementById('stop1');
var s2=document.getElementById('stop2');
var s1s = s1.getAttribute('style');
var s2s = s2.getAttribute('style');
s1.setAttribute('style', s2s);
s2.setAttribute('style', s1s);
}
]]>
</script>
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop id="stop1" offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop id="stop2" offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
</defs>
<circle id="bubble" cx="30" cy="30" r="20" stroke="black" stroke-width="2" fill="url(#grad1)" onclick="flipGrad(evt)" />
</svg>
Related
How to add text inside an SVG element
I would like to add text inside an element such that my intro is behind a rectangular, transparent element. I tried adding text inside an SVG like this <svg height="150" width="500"> <text x="100" y="30" fill="red">I love SVG!</text> <defs> <radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%"> <stop offset="0%" style="stop-color:rgb(255,255,255); stop-opacity:0" /> <stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" /> </radialGradient> </defs> <ellipse cx="200" cy="70" rx="85" ry="55" fill="url(#grad1)" /> </svg> However I can't Send the object backwards. Is this even possible? Is there a better approach I can use to meet this objective rather than the solution I'm trying to use?
Are you just talking about whether the text is behind the oval, or on top of it? If so, just changing the order of elements fixes that: <svg height="150" width="500"> <defs> <radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%"> <stop offset="0%" style="stop-color:rgb(255,255,255); stop-opacity:0" /> <stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" /> </radialGradient> </defs> <ellipse cx="200" cy="70" rx="85" ry="55" fill="url(#grad1)" /> <text x="100" y="30" fill="red">I love SVG!</text> </svg> Or is there something else you're trying to do? (Please note, even with the text on top of the oval, the contrast isn't that great, and it's still hard to read.)
SVG Filter does not binds to its icon
I have a simple SVG icon, where I need to set the linearGradient for it. I'm trying to make it by specifying id of filter to the SVG body, but it does not wok, because in such way I got an empty SVG icon at all... Why? <svg xmlns="http://www.w3.org/2000/svg" class="default___3oPC0 " style=" fill: url(#chat); filter: url(#chat); " fill="black" stroke="black" stroke-width="0" width="21px" height="21px" viewBox="0 0 17 17"> <filter id="chat"> <linearGradient> <stop offset="0" stop-color="#cecebf"></stop> <stop offset="1" stop-color="#9b9b8c"></stop> </linearGradient> </filter> <path d="M7.08,3a6.14,6.14,0,0,0-2.14.37,5.34,5.34,0,0,0-1.68,1A3.64,3.64,0,0,0,1.89,7.07a3.26,3.26,0,0,0,.44,1.62,4.48,4.48,0,0,0,1.33,1.42,2,2,0,0,1,.83,1.38,2.7,2.7,0,0,1,0,.28l.12-.12A1.8,1.8,0,0,1,6,11.07h.24a6.11,6.11,0,0,0,.86.06,6.43,6.43,0,0,0,2.15-.37,5.29,5.29,0,0,0,1.67-1,3.62,3.62,0,0,0,1.38-2.74A3.62,3.62,0,0,0,10.9,4.33a5.29,5.29,0,0,0-1.67-1A6.19,6.19,0,0,0,7.08,3Zm0-2h0C11,1,14.17,3.72,14.17,7.07S11,13.14,7.08,13.14A10,10,0,0,1,6,13.07,6.57,6.57,0,0,1,.94,15v-.39A2.89,2.89,0,0,0,2.66,12.2a2.73,2.73,0,0,0,0-.41A5.78,5.78,0,0,1,0,7.07C0,3.72,3.17,1,7.08,1ZM14.7,14.6a2.32,2.32,0,0,0,1.36,2.06V17a5.46,5.46,0,0,1-4.24-1.66,7.5,7.5,0,0,1-1,.06,6.87,6.87,0,0,1-3.74-1.07,8.79,8.79,0,0,0,5.68-2.05A7.09,7.09,0,0,0,14.6,10a6.45,6.45,0,0,0,.69-2.91c0-.16,0-.33,0-.49A4.81,4.81,0,0,1,17,10.2a4.93,4.93,0,0,1-2.28,4C14.71,14.36,14.7,14.48,14.7,14.6Z"></path> </svg>
You might need to apply the fill to the path using an ID attached to the gradient svg { height: 200px; } path { fill: url(#chat); } <svg xmlns="http://www.w3.org/2000/svg" class="default___3oPC0" viewBox="0 0 17 17"> <linearGradient id="chat"> <stop offset="0" stop-color="#cecebf"></stop> <stop offset="1" stop-color="#9b9b8c"></stop> </linearGradient> <path d="M7.08,3a6.14,6.14,0,0,0-2.14.37,5.34,5.34,0,0,0-1.68,1A3.64,3.64,0,0,0,1.89,7.07a3.26,3.26,0,0,0,.44,1.62,4.48,4.48,0,0,0,1.33,1.42,2,2,0,0,1,.83,1.38,2.7,2.7,0,0,1,0,.28l.12-.12A1.8,1.8,0,0,1,6,11.07h.24a6.11,6.11,0,0,0,.86.06,6.43,6.43,0,0,0,2.15-.37,5.29,5.29,0,0,0,1.67-1,3.62,3.62,0,0,0,1.38-2.74A3.62,3.62,0,0,0,10.9,4.33a5.29,5.29,0,0,0-1.67-1A6.19,6.19,0,0,0,7.08,3Zm0-2h0C11,1,14.17,3.72,14.17,7.07S11,13.14,7.08,13.14A10,10,0,0,1,6,13.07,6.57,6.57,0,0,1,.94,15v-.39A2.89,2.89,0,0,0,2.66,12.2a2.73,2.73,0,0,0,0-.41A5.78,5.78,0,0,1,0,7.07C0,3.72,3.17,1,7.08,1ZM14.7,14.6a2.32,2.32,0,0,0,1.36,2.06V17a5.46,5.46,0,0,1-4.24-1.66,7.5,7.5,0,0,1-1,.06,6.87,6.87,0,0,1-3.74-1.07,8.79,8.79,0,0,0,5.68-2.05A7.09,7.09,0,0,0,14.6,10a6.45,6.45,0,0,0,.69-2.91c0-.16,0-.33,0-.49A4.81,4.81,0,0,1,17,10.2a4.93,4.93,0,0,1-2.28,4C14.71,14.36,14.7,14.48,14.7,14.6Z"></path> </svg>
directed gradient in svg's path element
I'm writing a simple webpage that displays a graph and shows dependencies. I found an unexpected behavior in how path elements are rendered within svg. Here's the full HTML of the example: <html> <body> <svg id="svgConnections" xmlns="http://www.w3.org/2000/svg" style="width: 300px; height: 120px"> <defs> <linearGradient id="grad1" > <stop offset="0%" style="stop-color:yellow;stop-opacity:1" /> <stop offset="100%" style="stop-color:red;stop-opacity:1" /> </linearGradient> </defs> <path d="M40,40 L100,100 Z" stroke="url(#grad1)" strokeWidth="1px" /> <path d="M200,100 L140,40 Z" stroke="url(#grad1)" strokeWidth="1px" /> </svg> </body> </html> The same example is on https://jsfiddle.net/4fLjm0e2/ What bugs me is that the first line, which goes from top left to bottom right corner, looks exactly like the second line, which goes "in reverse": from bottom right corner to the top left. How do I make the path always start with yellow and end with red?
This is not a bug. This is problem in understanding. The default behavior of a linear gradient is to transition along a horizontal line from the left side of an object to its right side. It doesn't matter if you draw a path from left to right or from right to left. In both cases gradient will appear as from left to right as per default settings. Consider the demo below: <svg width="120" height="120" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="grad1" gradientUnits="userSpaceOnUse"> <stop offset="0%" style="stop-color:yellow;stop-opacity:1" /> <stop offset="100%" style="stop-color:red;stop-opacity:1" /> </linearGradient> </defs> <g stroke-width="2"> <path d="M10,40 L110,40 Z" stroke="url(#grad1)" /> <path d="M110,70 L10,70 Z" stroke="url(#grad1)" /> </g> </svg> If you wants the transition of colors to occur across a vertical line or a line at an angle, you must specify the line's starting point with the x1 and y1 attributes and its ending points with the x2 and y2 attributes. Rather than duplicate the stops into each <linearGradient> element, we'll use the xlink:href attribute to refer to the original gradient. The stops will be inherited, but the x- and y-coordinates will be overridden by each individual gradient. <linearGradient id="grad1" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" /> </linearGradient> <linearGradient id="grad2" xlink:href="#grad1" x1="1" y1="1" x2="0" y2="0"></linearGradient> Extending the above example: <svg width="120" height="120" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="grad1" gradientUnits="userSpaceOnUse"> <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" /> </linearGradient> <linearGradient id="grad2" xlink:href="#grad1" x1="120" y1="0" x2="0" y2="0"></linearGradient> </defs> <g stroke-width="2"> <path d="M10,40 L110,40" stroke="url(#grad1)" /> <path d="M110,70 L10,70 Z" stroke="url(#grad2)" /> </g> </svg> As in your example, you are using diagonal paths so we need to override x1, y1, x2 and y2 attribute of both <linearGradient> elements. These values on the first <linearGradient> element will override the default left to right settings to produce a diagonal gradient from top left to the right bottom. While on the <linearGradient> element these values will change the direction of gradient i.e from bottom to top. Now we can apply these gradients to the respective paths. <svg width="300" height="120" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="grad1" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" /> </linearGradient> <linearGradient id="grad2" xlink:href="#grad1" x1="1" y1="1" x2="0" y2="0"></linearGradient> </defs> <g stroke-width="2"> <path d="M40,40 L100,100 Z" stroke="url(#grad1)" /> <path d="M200,100 L140,40 Z" stroke="url(#grad2)" /> </g> </svg> Note: This Question can be useful in context with the current problem.
Equivalent SVG transform for new coordinate system
SVG is embedded in an object tag which has some dimensions (w1,h1). The user can zoom and pan to an arbitrary region in the SVG and wants to retain that view. I have the transform of the SVG with old dimensions. How to compute the transformation for the SVG to retain the previous view for the new dimensions(w2, h2)? Here is an example HTML, the SVG is embedded in the object tag In this example, (w1,h1) are (100%,100%) and (w2,h2) can be (50% ,50%) <html> <body> <object class="auto-capital-svg-content" data="1.svg" width="100%" height="100%" type="image/svg+xml"> </object> </body> </html> Here is a sample SVG <?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" height="150" width="400" style="" > <defs> <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1"></stop> <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1"></stop> </linearGradient> </defs> <g id="viewport" transform="matrix(6,0,0,6,200,200)"> <ellipse cx="200" cy="70" rx="85" ry="55" fill="url(#grad1)"></ellipse> <text fill="#ffffff" font-size="45" font-family="Verdana" x="150" y="86">SVG</text> </g> </svg> This is what I thought : calculate the scale based on the new/old dimensions such that aspect ratio is preserved and scale the transform. But this did not take care of the translation (tx,ty) in the matrix {a,b,c,d,tx,ty}.What should be done here?
Smoothly change the filling of a SVG
I'm using javascript to change the color of a svg. This changes my <linearGradient> filling : My problem is, that it is changing very rapidly. Is there any way to have a "smooth" flow between the colors? I tried to use the jquery anim() method but it wouldn't work because of the SVG-DOM. Edit: More source code. In the end, it's pretty simple. I get the stop elements of my svg and calculate a new rgb value. I then set the rgb value as the new stop-color of the stop element js: var gradient = $('#upper').children('stop'); var firstValue = 'stop-color:rgb('+top[0]+','+top[1]+','+top[2]+')'; var secondValue = 'stop-color:rgb('+bottom[0]+','+bottom[1]+','+bottom[2]+')'; gradient[0].setAttribute('style',firstValue); gradient[1].setAttribute('style',secondValue); html <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"> <defs> <linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%"> <stop style="stop-color:rgb(107,186,112);stop-opacity:1" offset="0"/> <stop style="stop-color:rgb(107,186,112);stop-opacity:1" offset="1"/> </linearGradient> </defs> <rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6" /> </svg>
Depending on the actual scenario, this could be solved with pure SMIL animation: <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"> <defs> <linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%"> <stop stop-color="rgb(107,186,112)" offset="0"> <animate attributeType="CSS" attributeName="stop-color" id="animate0" dur="2s" begin="rect0.click" fill="freeze" to="rgb(0,255,0)"/> </stop> <stop stop-color="rgb(107,186,112)" offset="1"> <animate attributeType="CSS" attributeName="stop-color" dur="2s" begin="animate0.begin" fill="freeze" to="rgb(255,0,0)"/> </stop> </linearGradient> </defs> <rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6" id="rect0"/> </svg> I this case the animation is triggered by a click on the rectangle. Specific colors can also be set by JavaScript and the animation can be triggered by JavaScript: <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none"> <defs> <linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%"> <stop stop-color="rgb(107,186,112)" offset="0"> <animate attributeType="CSS" attributeName="stop-color" id="animate0" dur="2s" begin="indefinite" fill="freeze"/> </stop> <stop stop-color="rgb(107,186,112)" offset="1"> <animate attributeType="CSS" attributeName="stop-color" dur="2s" begin="animate0.begin" fill="freeze"/> </stop> </linearGradient> </defs> <rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6"/> <script type="text/javascript"> var gradientColors = [[255,0,0],[255,255,0]]; var animateElements = document.getElementsByTagName("animate"); for (var i=0; i<2; i++) { animateElements[i].setAttribute( "to", "rgb("+gradientColors[i].join(",")+")" ); } animateElements[0].beginElement(); </script> </svg> Whether these solutions are practical for you depends on whether the targeted browsers support SMIL animation.