SGV Gradients Darker at Stops - html
I am trying to make a spherical image of a planet with a night side in shadow, to be rendered in browsers for a simulation web page, but I have been fighting a strange visual problem that I cannot seem to figure out.
Here is an image snip of the SVG as it is displayed in Chrome (this happens in IE also) and then zoomed up 3x:
You will notice that just above the day/night terminator, there is a faint darker horizontal band, about 1/3rd as wide as its distance above the terminator. That should not be there and I cannot figure why it is there or how to get rid of it.
Here is the SVG code in an HTML Snippet for easy viewing:
<div style="position:absolute; z-index:1; margin:15px;
width:640px; height:640px;
background-color:black">
<svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- NOTE: All internal units are in KM (or %) -->
<defs id="defsEa">
<clipPath id="svgEaClip">
<rect width="100%" height="100%" />
</clipPath>
<linearGradient id="lgdEaSphere">
<stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00" id="stopEarthCenter" />
<stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30" id="stopEarthInner" />
<stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31" id="stopEarthMid" />
<stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264" id="stopEarthOuter" />
<stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264" id="stopAirInner" />
<stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00" id="stopAirOuter" />
</linearGradient>
<radialGradient id="rgdEaSphere" xlink:href="#lgdEaSphere"
gradientUnits="userSpaceOnUse"
cx="0" cy="0"
fx="0" fy="0"
r="3339.05"
/>
<linearGradient id="lgdEaNightSide"
x1="0%" y1="0%" x2="0%" y2="100%"
spreadMethod="pad" >
<stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight" />
<!-- this stop seems to cause the artifact -->
<stop style="stop-color:#000000;stop-opacity:0.6;" offset="0.99" id="stopEaDusk" />
<stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator" />
</linearGradient>
</defs>
<g id="gEaAll" transform="scale(1.2,1.2)" >
<g id="gEaSunFacing" >
<!-- contains everything that stays oriented with the Sun -->
<circle
id="cEarthAir"
cx="0" cy="0" r="3339.05"
style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
<!-- overlay to give Earth a night side. -->
<path id="pNight"
style="stroke:none; fill:url(#lgdEaNightSide)"
d="M -3189.05,0
A 3189.05,15945.25 0 1,1 3189.05,0
Z"
/>
</g>
</g>
</svg>
</div>
(If you want you can save this to an HTML (.html) file (add the html and body tags) and then run it in your browser to see it. Or save it to an SVG file (.svg), and remove the div and br tags, if you want to use any SVG tools on it.
This artifact is occurring right at where one of the linear gradient stops are, specifically #stopEaDusk at line 33, in the linear gradient #lgdEaNightSide, which is being used as the fill for the overlay path #pNight. This artifact may seem very faint, but it is real and more pronounced when not zoomed out by 300%. My actual SVG also has a complimentary #pDay overlay that I removed to simplify the code. But when it is added, I get three of these darkness-band artifacts, the one above, one at the corresponding spot on the other side (where I have another dawn/dusk stop) and one right at the midpoint where the two overlays meet. When they are all three visible, it looks horrible, so I cannot just ignore it.
Other points:
As far as I can tell, this is not just a visual illusion as suggested here, as it persists even when blown up. And if it is, I still need to fix it.
I do not believe that it is this old Chrome bug as suggested in this answer, because it appears differently, but mostly because I see it in IE also (though it might be fainter).
tl,dr: What is causing this darkness band and how can I get rid of it?
There seems to be some confusion about what the question is here, so allow me to clarify. The linear gradient lgdEaNightSide was written for an explicit effect, which it seems to be doing, but it is also producing an undesirable side-effect which is not intended, and to the best of my understanding of SVg should NOT be happening.
The desired effect is that the pNight overlay adds:
1. 70% darkness, at the top of its arc (which is very high and off-screen), shading smoothly to 60% darkness 99% of the way down.
2. Then shading quickly from 60% darkness 99% of the way down, to 50% darkness 100% of the way down. This is to provide a dusk-like transition area.
However, in addition to that, it is also adding the undesired effect:
- at about 98.8% of the way down, it gets suddenly darker
- then at about 99.2% of the way down, it gets suddenly lighter again.
This should not be happening, and as far as I know, the SVG I wrote should not be doing this.
What I am looking for is how to keep the first (desired) effect, while getting rid of the second (undesired) effect.
I do not need to be told that the second stop is causing the problem. I know that, I stated it above and I pointed it out in the code comments. I do not need an answer to tell me what I already know. What I am looking for is:
how to fix it while retaining the visual effect that I originally wrote it for, and
Why is it happening, when AFAIK, it should not be happening.
There is nothing wrong with the code.
The banding exists because monitors do not have enough colors to represent the subtle change. You are trying to change 10% opacity over a large area which is not possible with most monitors.
You can determine this is a monitor issue by switching screens. Depending on the quality and the pixel density of the monitor, you will see slightly different results. Some browsers use dithering, which reduces the color banding with the tradeoff of pixelation.
The only way to work around the problem is to introduce more color variation like Prince was getting at.
An answer from another post does a great job of explaining this concept. It's worth a read, but I will quote the solutions to save people a click. https://stackoverflow.com/a/11274627/6794536
If you want to make it less obvious with your current 24-bit color
monitor, you can:
Change your design to introduce a subtle color shift into your
gradient (e.g. from dark blue to gray-green). This causes different
RGB channels bits can transition at different times, breaking up your
bands into a smaller differentiated colors.
Change your design to increase your dynamic range (e.g. from pure
white to pure black) so that you have more color bars to work with.
Change your design to reduce the overall distance that the gradient
occurs over, reducing the widths of the bands.
…or some combination of the above.
I implemented this strategy, but it is not perfect. If you really want to get rid of color banding, you will have to make other tradeoffs. For example, you may not be able to use pure black.
<div style="position:absolute; z-index:1; margin:15px;
width:640px; height:640px;
background-color:#0c0c26">
<svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- NOTE: All internal units are in KM (or %) -->
<defs id="defsEa">
<clipPath id="svgEaClip">
<rect width="100%" height="100%" />
</clipPath>
<linearGradient id="lgdEaSphere">
<stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00" id="stopEarthCenter" />
<stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30" id="stopEarthInner" />
<stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31" id="stopEarthMid" />
<stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264" id="stopEarthOuter" />
<stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264" id="stopAirInner" />
<stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00" id="stopAirOuter" />
</linearGradient>
<radialGradient id="rgdEaSphere" xlink:href="#lgdEaSphere"
gradientUnits="userSpaceOnUse"
cx="0" cy="0"
fx="0" fy="0"
r="3339.05"
/>
<linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%" spreadMethod="pad">
<stop style="stop-color:#00033e;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight"></stop>
<!-- this stop seems to cause the artifact -->
<stop style="stop-color:#090d24;stop-opacity:0.6;" offset="0.99" id="stopEaDusk"></stop>
<stop style="stop-color:#000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator"></stop>
</linearGradient>
</defs>
<g id="gEaAll" transform="scale(1.2,1.2)" >
<g id="gEaSunFacing" >
<!-- contains everything that stays oriented with the Sun -->
<circle
id="cEarthAir"
cx="0" cy="0" r="3339.05"
style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
<!-- overlay to give Earth a night side. -->
<path id="pNight"
style="stroke:none; fill:url(#lgdEaNightSide)"
d="M -3189.05,0
A 3189.05,15945.25 0 1,1 3189.05,0
Z"
/>
</g>
</g>
</svg>
</div>
It is because you are using an extra stop value in the gradient of overlay. As you are using linear gradient(lgdEaNightSide) in overlay to give earth a night side as below.
You can see you are using two offset values, one at 0.99 and other at 1. This is the reason you are getting faint darker horizon band.
<linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%" spreadMethod="pad">
<stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight" />
<!-- this stop seems to cause the artifact -->
<stop style="stop-color:#000000;stop-opacity:0.6;" offset="0.99" id="stopEaDusk" />
<stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator" />
</linearGradient>
Just delete an extra stop value and it will work, please find working code below:
<div style="position:absolute; z-index:1; margin:15px;
width:640px; height:640px;
background-color:black">
<svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- NOTE: All internal units are in KM (or %) -->
<defs id="defsEa">
<clipPath id="svgEaClip">
<rect width="100%" height="100%" />
</clipPath>
<linearGradient id="lgdEaSphere">
<stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00"
id="stopEarthCenter" />
<stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30"
id="stopEarthInner" />
<stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31"
id="stopEarthMid" />
<stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264"
id="stopEarthOuter" />
<stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264"
id="stopAirInner" />
<stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00"
id="stopAirOuter" />
</linearGradient>
<radialGradient id="rgdEaSphere" xlink:href="#lgdEaSphere"
gradientUnits="userSpaceOnUse" cx="0" cy="0" fx="0"
fy="0" r="3339.05" />
<linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%"
spreadMethod="pad">
<stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00"
id="stopEaMidnight" />
<!-- this stop seems to cause the artifact -->
<stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00"
id="stopEaTerminator" />
</linearGradient>
</defs>
<g id="gEaAll" transform="scale(1.2,1.2)">
<g id="gEaSunFacing">
<!-- contains everything that stays oriented with the Sun -->
<circle id="cEarthAir" cx="0" cy="0" r="3339.05"
style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
<!-- overlay to give Earth a night side. -->
<path id="pNight" style="stroke:none; fill:url(#lgdEaNightSide)" d="M -3189.05,0
A 3189.05,15945.25 0 1,1 3189.05,0
Z" />
</g>
</g>
</svg>
</div>
Related
SVG Path with linearGradient is not displayed with certain combinations
I'm using this code snippet below: <?xml version="1.0" encoding="UTF-8"?> <html> <body> <svg width="900" height="300"> <g transform="translate(10,10)"> <defs> <linearGradient id="gradient0" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" stop-color="#05a" /> <stop offset="100%" stop-color="#0af" /> </linearGradient> </defs> <g> <g class="link"> <path class="gradient0" d="M36,5.50458742 C440,5.504589729,440,5.504587889,844,5.504587429" stroke-width="12" style="stroke: url('#gradient0');"> </path> </g> </g> </g> </svg> </body> </html> The numbers are generated automatically by D3. So I have no control over the exact numbers inside the Path d attribute. As mentioned above, the graph is not displayed. But if I modify the value even just a little bit, the graph is then displayed. For example, if I increment only the initial path position from:d="M36,5.50458742 to d="M36,5.50458743, then the graph is displayed. It's not just for the initial position. If I increment only the "C" path attribute value from: C440,5.504589729 to C440,5.504589730, the graph is also displayed. What is the problem here? The Y position value is definitely a positive number, so it should've been displayed. The graph is also displayed if I do not use linearGradient. If I modify only the stroke value from: stroke: url('#gradient0'); to stroke: black;, the graph is also displayed. I'm using Chrome browser.
Chrome not rendering SVG as exptected
I have a problem with rendering SVG image, but only in Chrome (version 87). It did work in early version of Chrome, like version 86. It does render as it should be in Firefox. The URL of the SVG is: https://svgshare.com/i/RuQ.svg At Chrome, the sky-blue circle is not displayed, While in Firefox it does. Also, here's a simple HTML page with how I use the SVG. <!DOCTYPE html> <html> <head> </head> <body> <img height="50px" width="50px" src="https://svgshare.com/i/RuQ.svg"></img> </body> </html> Any ideas for what might be wrong in the SVG?
This SVG looks like it was produced by Adobe Illustrator. While Robert Longson is formally right that this is a Chrome bug, the blame goes fully to Adobe. For documentation: this is what the program exported to SVG. I have already shortened the code by ca. 50% removing unused definitions: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 43 43"> <defs> <style> .cls-3{fill:#040f21;} .cls-4{mask:url(#mask-2);} .cls-5{fill:url(#linear-gradient);} .cls-7{filter:url(#luminosity-noclip-2);} </style> <filter id="luminosity-noclip-2" x="2.36" y="-16315" width="39.63" height="32766" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feFlood flood-color="#fff" result="bg"/> <feBlend in="SourceGraphic" in2="bg"/> </filter> <mask id="mask-2" x="2.36" y="-16315" width="39.63" height="32766" maskUnits="userSpaceOnUse"> <g class="cls-7"/> </mask> <linearGradient id="linear-gradient" x1="22.17" y1="60.87" x2="22.17" y2="-17.54" gradientUnits="userSpaceOnUse"> <stop offset="0.21" stop-color="#3f97f5"/> <stop offset="0.83" stop-color="#3fa9f5"/> </linearGradient> </defs> <title>Dlogo</title> <g id="Layer_1" data-name="Layer 1"> <path class="cls-3" d="M28.43,31.83a23.24,23.24,0,0,1-5.41.78c-6.12,0-8.86-2.07-8.86-11.48S17.44,9.44,23.34,9.44a18.52,18.52,0,0,1,5.09.71l-.51,3.28a16.24,16.24,0,0,0-4.22-.61c-3.62,0-5.16,1.13-5.16,8.18S20,29.22,23.7,29.22a15.69,15.69,0,0,0,4.22-.67Z"/> <g class="cls-4"> <path class="cls-5" d="M22.17,41.31A19.81,19.81,0,1,1,42,21.5,19.83,19.83,0,0,1,22.17,41.31Zm0-35.21a15.4,15.4,0,1,0,15.4,15.4A15.41,15.41,0,0,0,22.17,6.1Z"/> </g> </g> </svg> The second path is the only member of a group .cls-5, which is masked with #mask-2. This luminosity mask contains an empty group, making the mask by default hiding all content that is masked. To counteract this, the group is filtered with #luminosity-noclip-2. The filter floods an area in user space of 39.63 by 32766 with white, then copies the group contents (nothing) on top. As a result, the luminosity mask should be filled white, making all masked content visible, completely unaltered. In short: nothing but useless junk. In theory, it does nothing. In practice, it wastes computing resources and gives oportunity to almost every bug renderers might have to bite you. If this is what Illustrator produces nowadays, throw it out of the window to never use again. A clean SVG that is identical in all aspects to the intended one looks like this: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 43 43"> <linearGradient id="linear-gradient" x1="22.17" y1="60.87" x2="22.17" y2="-17.54" gradientUnits="userSpaceOnUse"> <stop offset="0.21" stop-color="#3f97f5"/> <stop offset="0.83" stop-color="#3fa9f5"/> </linearGradient> <title>Dlogo</title> <path style="fill:#040f21;" d="M28.43,31.83a23.24,23.24,0,0,1-5.41.78c-6.12,0-8.86-2.07-8.86-11.48S17.44,9.44,23.34,9.44a18.52,18.52,0,0,1,5.09.71l-.51,3.28a16.24,16.24,0,0,0-4.22-.61c-3.62,0-5.16,1.13-5.16,8.18S20,29.22,23.7,29.22a15.69,15.69,0,0,0,4.22-.67Z"/> <path style="fill:url(#linear-gradient);" d="M22.17,41.31A19.81,19.81,0,1,1,42,21.5,19.83,19.83,0,0,1,22.17,41.31Zm0-35.21a15.4,15.4,0,1,0,15.4,15.4A15.41,15.41,0,0,0,22.17,6.1Z"/> </svg>
SVG stroke url: linearGradient is working but solidColor is not
I've tried to set the stroke color. It works using linearGradient but it doesn't work using solidColor: <svg class="svg_defs" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="color_1"> <stop offset="0" stop-color="red" stop-opacity="1"/> </linearGradient> <solidColor id="color_2" solid-color="blue" solid-opacity="1"/> <linearGradient id="half"> <stop offset="50%" stop-color="green" stop-opacity="0.5"/> <stop offset="50%" stop-color="green" stop-opacity="0"/> </linearGradient> </defs> </svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 101 101" width="50"> <rect x="0" width="49%" height="99%" fill="url(#half)" stroke-width="2" stroke="url(#color_1)"/> <rect x="50%" width="49%" height="99%" fill="url(#half)" stroke-width="2" stroke="url(#color_2)"/> </svg> The rect referencing color_1 is working, but not the second one referencing color_2. Even the example on w3.org isn't working. The link should show forms in red. This happens (or doesn't happen) in Firefox, Chrome and Safari. Is there something missing? Try it: jsFiddle
solidColor is not part of SVG 1.1, it is part of SVG 1.2 tiny and in a slightly altered and non-backwards compatible way it's proposed to be part of the upcoming SVG 2 specification. I've a patch for Firefox that would implement solidColor but the only other UA that ever supported this was the now obsolete Opera version 12. solidColor seems rather unnecessary given that it can be emulated either with a single stop gradient or via CSS variables. If you can persuade some other UAs to consider implementing it my Firefox patch can land.
SolidColor isn't support by any browser. It's also mentioned in the svg2 working draft, but I don't have a clue when it will be implemented. Alternative you could use: <rect x="50%" width="49%" height="99%" fill="url(#half)" stroke-width="2" stroke="blue" stroke-opacity="1"/> or <linearGradient id="color_2"> <stop offset="0%" stop-color="blue" stop-opacity="1"/> <stop offset="100%" stop-color="blue" stop-opacity="1"/> </linearGradient>
Call external svg in to another svg
I've got multiple inline svgs. In all of those there is a common path + an image. Usually this common part is supposed to change regularly. So if I save the common area as a separate svg file. Is it possible to call the common svg file in to another inline svg? E.g.: main.svg <svg height="130" width="500"> <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 offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" /> </linearGradient> </defs> <ellipse cx="100" cy="70" rx="85" ry="55" fill="url(#grad1)" /> //I need to include external.svg here </svg> external.svg <text fill="#ffffff" font-size="45" font-family="Verdana" x="50" y="86">SVG</text> <image width="20" height="20" xlink:href="man.png"></image>
If you are embedding the SVGs using an <img> (or background-image), then those SVGs must be self contained. They cannot reference other external files whether they be CSS, images, fonts or other SVGs. However it should work if you use inlined SVGs, <object> etc.
Is it possible to load an external SVG fill into an inline-svg element?
I'd like to create an inline-svg text element with a fill attribute loading a gradient defined in an external svg document. Inline SVG <svg> <text fill="url(/path/to/svg.svg#gradient)">Mask this text</text> </svg> /path/to/svg.svg <?xml version="1.0" encoding="utf-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:#ffeab4; stop-opacity:1" /> <stop offset="30%" style="stop-color:#ed9f3b; stop-opacity:1" /> <stop offset="40%" style="stop-color:#ed9f3b; stop-opacity:1" /> <stop offset="70%" style="stop-color:#ffeab4; stop-opacity:1" /> <stop offset="100%" style="stop-color:#ed9f3b; stop-opacity:1" /> </linearGradient> </defs> </svg> Here is a jsfiddle test case and here is a gist of the SVG gradient I'm trying to load. Question Is it possible to load an external fill into an inline-svg element? Have I just defined my external gradient incorrectly or am referencing it incorrectly?
Using external fill and stroke is allowed by the SVG spec, but not all browsers implement that part. Opera and Firefox does, but not Chrome for example. A live example here. The syntax you describe is correct. E.g fill="url(example.svg#gradient)" will fetch the external example.svg and use the specified gradient from that file.