Using Raphael, I noticed that if I tried to apply a radial fill on a circle using 0.9 and 0.2 as the radial focus points, it fails to draw the radial fill.
paper.circle(50,300,20).attr({"fill":"r(0.5,0.1)#f00-000"});
paper.circle(100,300,20).attr({"fill":"r(0.9,0.2)#f00-000"}); // <-- fails
paper.circle(150,300,20).attr({"fill":"r(0.9,0.3)#f00-000"});
I've set up a fiddle, here, and did a 10x10 grid, and the (0.9,0.2) is the only one that failed.
I'd like to understand why. http://jsfiddle.net/ENMry/2/
This is not problem of Raphael library but most probably of JS SVG rendering. You can repeat the same problem using just JavaScript and SVG markup without Raphael library. See example at jsBin
I changed your example to have bigger circles with 11x11 grid (from 0.0 to 1.0) and put also one row separately on the top to show how is focus point moving. See example at jsBin.
Using browser console (ctrl+shift+J in Chrome) you can inspect DOM elements. The following markup is set for our white element (the 2nd one in the first row):
<radialGradient id="2r_0.1_0.2__f00-_000" fx="0.1" fy="0.2" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
<stop offset="0%" stop-color="#ff0000" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></stop>
<stop offset="100%" stop-color="#000000" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></stop>
</radialGradient>
<circle cx="175" cy="50" r="30" fill="url(#2r_0.1_0.2__f00-_000)" stroke="#000" opacity="1" fill-opacity="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); opacity: 1; fill-opacity: 1;">
</circle>
This MDN Gradients tutorial explains how radial gradient works.
If I understand correctly radial gradient is defined with 5 data and stop markup:
where is point of radiation: for example cx="0.25" cy="0.25" r="0.25". They define position and where the gradient ends. If not set cx and cy are in the midle of the element (same as value 0.5 or 50%; cx=0, cy=0 means top-left), r is 1 or 100%. In our case radiation starts in the middle of the circle and ends on its edge.
stop markup defines which colors should be at certain position.
where is focal (focus) point: in our case fx="0.1" fy="0.2". Standard says: fx and fy define the focal point for the radial gradient. The gradient will be drawn such that the 0% gradient stop is mapped to (fx, fy).
If you imagine a rectange around the circle, fx="0.1" fy="0.2" is somewhere to the left upper corner. This tutorial says: If the focal point is moved outside the circle described earlier, its impossible for the gradient to be rendered correctly, so the spot will be assumed to be on the edge of the circle. If the focal point isn't given at all, its assumed to be at the same place as the center point.
The first circle in the top row has fx="0.0" fy="0.2" and is out of radiation circle. So the spot is set on the edge: left, middle.
The "problematic" white circle has fx="0.1" fy="0.2" and this point is exactly on the edge of (radiation) circle. And rendering somehow fails. The same is for fx="0.9" fy="0.2", fx="0.2" fy="0.1" and fx="0.2" fy="0.9".
Following the same logic we should have another 4 white circles:
fx="0.8" fy="0.1"
fx="0.8" fy="0.9"
fx="0.1" fy="0.8"
fx="0.9" fy="0.8"
but they are rendered correctly.
You can easily see all those "problematic" points if you draw a circle and a grid.
So, I do not know if this is some rounding problem or something else. Anyway, it could be a bug. I found one connected with radial rendering but it is not exactly the same.
BTW, FireFox and IE10 render it without problem.
Note: I submit an issue 322487
Related
I'd like to take advantage of symmetry in my SVG designs, but most viewers (including Firefox and Chromium browsers) render gaps / lines where the numbers clearly tell me there shouldn't be any. Using zoom to render those designs bigger reduces the amount and thickness of those wrongly rendered lines.
Am I misunderstanding the svg format?
Is it too resource intensive to implement svg rendering mathematically correct?
Or does nobody care for such edge cases?
An example to better illustrate what I'm talking about:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="98" height="98" fill="red" fill-opacity="0.7">
<g id="2">
<g id="4">
<path id="p" d="m49,35c-3,0 -4.575713,0.605785 -6.9934,1.888339L31.330807,18.397364 37,15C38,7.76 38,6 39,0.7 39.7,0 44,0 49,0Z"/>
<use xlink:href="#p" transform="rotate(-60, 49, 49)"/>
<use xlink:href="#p" transform="translate(98) scale(-1, 1) rotate(60, 49, 49)"/>
</g>
<use xlink:href="#4" transform="translate(98) scale(-1, 1)"/>
</g>
<use xlink:href="#2" transform="rotate(180, 49, 49)"/>
</svg>
I found here:
If two partially opaque shapes overlap, can I show only one shape where they overlap?
that it's possible to use filters to make overlapping shapes behave as if they weren't when it comes to opacity, but it still bugs me that workarounds like that are required. It increases complexity, file-size and decreases compatibility with viewers that don't implement those filters.
Application of that workaround to the example above:
<svg width="98" height="98" fill-opacity=".7" fill="red" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="f">
<feComponentTransfer>
<feFuncA type="table" tableValues="0 .7 .7" />
</feComponentTransfer>
</filter>
<g filter="url(#f)">
<g id="2">
<g id="4">
<path id="p" d="m51 35c-3-0-6 0-10 3l-15-15 11-7c2-8 1-8 2-14 0.7-0.7 7-0.7 12-0.7"/>
<use transform="rotate(-60 49 49)" xlink:href="#p"/>
<use transform="translate(98) scale(-1 1) rotate(60 49 49)" xlink:href="#p"/>
</g>
<use transform="translate(98) scale(-1 1)" xlink:href="#4"/>
</g>
<use transform="rotate(180 49 49)" xlink:href="#2"/>
</g>
</svg>
Think about how any sort of rendering engine has to render your SVG. It steps through the SVG, drawing one element at a time.
Unless that shape is a line or rectangle that hits pixel boundaries exactly, it needs to smooth the edges of that shape. It does that using a technique called ant-aliasing. It draws semi-transparent pixels to approximate an edge that only partially covers a pixel.
For example, if a shape covers exactly half a pixel, the renderer will draw the colour into that pixel with 50% alpha.
It then moves on and draws the next shape. Even if that shape has a mathematically congruent border, you probably won't end up with a perfect join.
Here's why.
Picture three adjacent pixels where the edge of a shape passes exactly halfway through the centre pixel.
+--------+--------+--------+
First shape drawn: | 100% | 50% | 0% |
+--------+--------+--------+
The percentages here represent the amount of the shape's colour that is drawn into each pixel. 100% in the left pixel. 50% colour (alpha) in the middle pixel. And no colour drawn into the right pixel.
Now imagine a second shape that shares a border with the first shape. You might imagine that the following is what happens.
+--------+--------+--------+
First shape drawn: | 100% | 50% | 0% |
+--------+--------+--------+
Second shape drawn: | 0% | 50% | 100% |
+--------+--------+--------+
Resulting image: | 100% | 100% | 100% |
+--------+--------+--------+
But that isn't what happens. The first shape has already been rendered out as pixels. The renderer has no memory about the shape of previous things it has drawn. It only has the colour of the previously rendered pixels to go by.
When it goes to draw the middle pixel, in either the first or second step, it blends the 50% new colour with what the pixel value already is. The formula will be roughly the following:
result = 0.5 * old_pixel_colour + 0.5 * new_pixel_colour
So, for example, let's take the pixel percentage examples from above and imagine we are drawing two red shapes onto a white background.
After the first shape is drawn, the pixels should look something like this.
rgb(255, 0, 0) rgb(255, 128, 128) rgb(255, 255, 255)
[0 * bg + 1.0 * red] [0.5 * bg + 0.5 * 50%_red] [1.0 * bg + zero_red]
Where bg represents the white background colour the pixels start with. And 50%_red means the 50% transparent red that antialiasing is using to represent a half-covered pixel.
After the second pass, the pixels will look something like this:
rgb(255, 0, 0) rgb(255, 192, 192) rgb(255, 0, 0)
[1.0 * first + no_red] [0.5 * first + 0.5 * 50%_red] [0 * first + 1.0 * red]
Where first represents the colour of the pixel after the first shape is drawn. I hope this makes sense.
Or in terms of percentage of colour (red).
+--------+--------+--------+
First shape drawn: | 100% | 50% | 0% |
+--------+--------+--------+
Second shape drawn: | 0% | 50% | 100% |
+--------+--------+--------+
Resulting image: | 100% | 75% | 100% |
+--------+--------+--------+
So I hope you can see why those border pixels can end up showing a faint white line. It's due to the fact that you are blending two layers of antialiased pixels. It's actually a light red line.
Theretically, a renderer could analyse the pixel coverage of a stack of shapes. But that is mathematically very complex. It would slow down the rendering process enormously.
The general explanation how anti-aliasing works has been given by Paul LeBeau. It is notable that you are able to turn this algorithm off with the shape-rendering presentation attribute set to crispEdges. The renderer will try to draw every pixel either fully opaque or fully transparent:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="98" height="98" fill="red" fill-opacity="0.7">
<g id="2" shape-rendering="crispEdges">
<g id="4">
<path id="p" d="m49,35c-3,0 -4.575713,0.605785 -6.9934,1.888339L31.330807,18.397364 37,15C38,7.76 38,6 39,0.7 39.7,0 44,0 49,0Z"/>
<use xlink:href="#p" transform="rotate(-60, 49, 49)"/>
<use xlink:href="#p" transform="translate(98) scale(-1, 1) rotate(60, 49, 49)"/>
</g>
<use xlink:href="#4" transform="translate(98) scale(-1, 1)"/>
</g>
<use xlink:href="#2" transform="rotate(180, 49, 49)"/>
</svg>
This might be helpful, depending on the use case, or not. Edges will appear "ragged", but that may be acceptable.
But that is not the end of the problems. In an ideal environment, the renderer would always decide that a pixel that sits on top of a "seam" either belongs to one side or the other. But in the real world of discrete numbers, it is possible it decides the pixel belongs to neither, and leave it transparent.
Especially if you have one or more transformations applied to a shape, an application might decide it has to round numbers multiple times to compute the final rendering, and rounding errors could add up. (In my experience, Chrome is more prone to such effects than other browsers.)
And finally, the specification leaves renderers with a lot of leeway in what they "might" (or not) do with that attribute value:
To achieve crisp edges, the user agent might turn off anti-aliasing for all lines and curves or possibly just for straight lines which are close to vertical or horizontal. Also, the user agent might adjust line positions and line widths to align edges with device pixels.
The conclusion, unfortunately, is that every renderer uses its own optimization strategy, and you will have no guarantee where you end up.
Background
In the SVG2 spec we can read that:
The opacity value used for the gradient calculation is the product of
the value of stop-opacity and the opacity of the value of stop-color.
For stop-color value types of that don't include explicit opacity
information, the opacity of that component must be treated as 1.
Question
Why does setting stop-opacity: 0 on a 100% gradient stop (for example), not make that gradient stop completely transparent?
You can see the green in the code below.
Pointers to text in the spec that explains this behavior, would be appreciated.
Code
<svg height="150" width="200">
<defs>
<radialGradient id="grad1" cx="50%" cy="50%" r="50%">
<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:0" />
</radialGradient>
</defs>
<ellipse cx="100" cy="75" rx="100" ry="75" fill="url(#grad1)" />
Sorry, your browser does not support inline SVG.
</svg>
EDIT 1
I was using Chrome for these tests. The opacity does work as expected in Firefox.
EDIT 2
Removed fx fy to demonstrate that problem still remains, even without those.
The SVG 1.1 spec doesn't say much on the subject of interpolating colors between stops. But all the renderers I have seen so far are in concordance with this SVG 2 spec statement:
For linear and radial gradients, the color value between two stops along the gradient vector is the linear interpolation, per channel, of the color for each stop, weighted by the distance from each stop.
It's not explicitely said that opacity is treated as a channel in this context, but again that is what renderers do: If there are two gradient stops with color1 and opacity=1 and color2 and opacity=0, then the color at an intermediate point is a linear interpolation between the color values, and the opacity is partial.
For your example color will change like this:
color stop at 0%: red 255, green 0, blue 0, opacity 1
color interpolation at 25%: red 192, green 64, blue 0, opacity 0.75
color interpolation at 50%: red 128, green 128, blue 0, opacity 0.5
color interpolation at 75%: red 64, green 192, blue 0, opacity 0.25
color stop at 100%: red 0, green 255, blue 0, opacity 0
As the color fades, it goes from red to green:
I can see this with almost all browsers I have access to: Chrome 70/Windows, Chromium 70/Debian, Firefox 60esr/Debian, Firefox 62/Debian, Edge, IE11. The only exeption is Firefox on Windows: v62/63 show a gradient going from opaque red to transparent red:
From my point of view, it is Firefox that is in error here.
I'm generating pie charts in SVG (using the ruby library svg-graph, but that isn't totally relevant), but there is a strange edge case where the chart is totally blank. This occurs in Chrome and Safari, but not in IE11 or Firefox.
I've narrowed it down to a certain path element whose d attribute varies slightly between the two. One produces a yellow circle while the other does not. My SVG knowledge is limited, so I don't understand why the second snippet isn't outputting anything. Any ideas?
Working:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path d="M109.0,109.0 L109.0,0.0 A109.0,109.0 0, 1,1, 108.99999000000007 0.0 Z"
transform="translate( -3.216245299353273e-15 10.0 )"
style="fill: #FFDC00" />
</g>
</svg>
Not working:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g>
<path d="M108.5,108.5 L108.5,0.0 A108.5,108.5 0, 1,1, 108.49999000000007 0.0 Z"
transform="translate( -3.216245299353273e-15 10.0 )"
style="fill: #FFDC00" />
</g>
</svg>
This may be a rounding issue. The path is trying to draw a circle using the arc path instruction "A". The beginning and ending points of the arc are very close together (< 0.0000001 units). If the SVG renderer thinks the two points are actually the same then it will draw an empty arc (0% of a circle) instead of a nearly complete (99.9999% of a circle).
You can try separating the beginning and ending points slightly further away (e.g., try 359 degrees instead of 360); as the Z instruction will close the path anyway and hide the tiny wedge left over. Also to see more of what's going on try stroking the path instead of filling it.
Or draw a circle using two half-circle arcs. See Circle drawing with SVG's arc path
I'm new in svg drawing.
Is there any option to colorize an svg <polyline> with gradient? I need to colorize only stroke, but all filters I founded is applied gradient both to stroke and body.
In fact I'm trying to make glow neon effect like this: http://screencloud.net/v/j2hE and it works fine for now with code below when I'm draw a strict line:
<linearGradient id="grad">
<stop offset="0%" stop-color="#ffd95d"/>
<stop offset="100%" stop-color="#ffd95d" stop-opacity="0" />
</linearGradient>
But when I'm drawing a line by circle, it looks like:
http://screencloud.net/v/9M6x (bottom is start and around circle to top where finish). As you can see, gradiend is applied to all polyform, but I need it to be gradiented only line as I draw it.
Is there any option to make neon glow lines like I need it?
For better understanding - I'm trying to get effect similar to default Windows screensaver named "glowing lines".
There is no way to apply a linearGradient along the length of a line's stroke.
The only way you could do it is to draw a sequence of individually coloured line/path segments that slowly fade out.
I am new to this. I want to make a clock similar to the one given here. But they have used images. Instead I want to make use of ARC. Does anyone know, how can we make an arc, that too using only css? Consider an example that I have to make an arc of 15 degrees. Any kind of suggestions are greatly appreciated.
Thanks in advance.....
I'd recommend you use SVG to do this. The markup is fairly simple, but you'll need to stay aware that there are four different rules which affect which side of the two end points the bulge of the arc with lay and if the bulge will try to take the "short way" or the "long way" with the given radius.
Here's an example. Use a stylesheet like this:
.arc{
fill:tan;
stroke:red;
stroke-width:4px;
}
combined with an svg path like this:
<svg width="100" height="100">
<path d="M 10,40 A 50,50 0 0 1 90,70"/>
</svg>
In short the commands in the above example are
M (move mode with absolute coordinates)
x,y (start the arc here here)
A (arc mode with absolute coordinates)
r1,r2 (the two radius of the ellipse that the arc goes around. use the same value twice for a circle)
z rotation (the rotation of that ellipse. 0 is no rotation)
large arc flag (see fiddle link below)
sweep flag (see fiddle link below)
x,y (where the arc will end)
I made this fiddle to demonstrate all the different combinations of the two flags in movement which helps me a lot in deciding which ones to use. http://jsfiddle.net/rgbk/avpye8nm
The W3C docs are here: http://www.w3.org/TR/SVG11/paths.html#PathDataEllipticalArcCommands They describe z rotation as "x-axis-rotation" which is wrong.
Does it have to be CSS ? or could you use HTL5 canvas?
http://www.w3schools.com/tags/canvas_arc.asp
then you could use that canvas to do the animation instead of using CSS animations...