SVG Circle rendering bug in Safari when circles are stacked - svg

I have an SVG made up of several circles. The goal is to have different circles filled using different percentages using the dash-array technique.
At the moment, only circle "one" (see circle tag with class="one") is "full". The other two ("two" and "three") are 0.
But am getting a strange artefact when the SVG is viewed in Safari. I can fix this if the make circle.one the last but order is important so I don't want to change, if possible.
Code pen: https://codepen.io/codeload/pen/KXvLEp
<svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 54 54">
<circle cx="27" cy="27" r="25" fill="#293741" fill-opacity=".75" stroke-width="4"></circle>
<circle cx="27" cy="27" r="25" fill="transparent" stroke-width="4" stroke-dasharray="156.067915 0" stroke-dashoffset="39.0169787" class="one"></circle>
<circle cx="27" cy="27" r="25" fill="transparent" stroke-width="4" stroke-dasharray="0 156.067915" stroke-dashoffset="0" class="two"></circle>
<circle cx="27" cy="27" r="25" fill="transparent" fill-opacity=".75" stroke-width="4" stroke-dasharray="0 156.067915" stroke-dashoffset="0" class="three"></circle>
</svg>

Related

Gap SVG circle/ellipse with vertical linecap edges

I am drawing an SVG ellipse and I want two gaps, positioned center top and bottom:
<ellipse fill="none" stroke="black" stroke-width cx="15" cy="15" rx="12.55" ry="13.45" stroke-dasharray="19.65 1.5 39.32 1.5"/>
But I want the gap edges to be vertical, like if I a straight line was drawn through the un-gapped ellipse:
<g stroke-width="1.5">
<ellipse stroke="black" fill="none" cx="15" cy="15" rx="12.55" ry="13.45"/>
<path stroke="white" d="M15 35 V30">
</g>
I can't do that, because the line is not transparent. The gaps should look like nothing has been drawn there and not alter or overlay the background.
Further unsuccessful experiments:
stroke-linecap="square" on example #1
stroke="transparent" for the line in ex. #2.
stroke="transparent" stroke-occupacy="1" for the line in ex. #2.
What can I do?
jsfiddle
This is a typical case for using a mask. Everything that coincides with the white parts of the mask is drawn as is, everything that coincides with the black parts becomes transparent.
<svg viewBox="0 0 40 40" height="90vh">
<mask id="mask">
<rect width="100%" height="100%" fill="white" />
<path stroke="black" stroke-width="5" d="M15 35 V0" />
</mask>
<ellipse stroke="black" fill="none" stroke-width="1.5"
mask="url(#mask)"
cx="15" cy="15" rx="12.55" ry="13.45" />
</svg>

SVG Path - render only a percent of the specified path

NOTE: I have a feeling that the answers to this question will rely on the dashed stroke trick often used to answer questions about animating svg path ( e.g. Animate svg path, and the countless other similar questions). If possible, I am looking for an answer that does not rely on dashed lines.
Suppose you have an SVG with a path between some number of objects. You know where the objects are centered and their width. In principal you could calculate a path that only goes from the the edges of each object. However, this can be tedious. So how can you specify a path form object 1 to object 2, but offset the drawn part so it is only shown from the edges?
In the code below, the first path between two points uses their center.
This is how I want to specify my path. The second path is how I would like the path to be rendered. e.g. I am looking for some attribute like offset-from-start=10 and offset-from-end=10 to do this. If the path is compound or if the object has an irregular shape, calculating the angle of the shorter path is tedious (but straightforward). In addition, using a shorter path, changes curves, where hopefully this offset does not.
<svg style="width:200px;height:200px">
<circle r="10" cx="10" cy="10" fill="red"></circle>
<circle r="10" cx="190" cy="10" fill="red" style="opacity:0.5"></circle>
<path d="M10 10 L 190 10" stroke-width="4" stroke="black"></path>
<circle r="10" cx="10" cy="50" fill="red"></circle>
<circle r="10" cx="190" cy="50" fill="red" style="opacity:0.5"></circle>
<path d="M20 50 L 180 50" stroke-width="4" stroke="black"></path>
</svg>
If you want to avoid using the stroke-dasharray property, how about simply hiding the part of the path that intersects your object? The straightforward way to do this is avoiding partial transparency and drawing the path below:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
style="width:200px;height:50px">
<path d="M10 10 L 190 10" stroke-width="4" stroke="black"></path>
<circle r="10" cx="10" cy="10" fill="red"></circle>
<circle r="10" cx="190" cy="10" fill="#ff8080"></circle>
</svg>
A bit more sophisticated would be to define a mask. For that, you need to first define any object inside the mask without styling (implicit black) on a white background, and then reuse them with the visual styles:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
style="width:200px;height:50px">
<mask id="mask" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white" />
<circle id="obj1" r="10" cx="10" cy="10" />
<circle id="obj2" r="10" cx="190" cy="10" />
</mask>
<use xlink:href="#obj1" fill="red" />
<use xlink:href="#obj2" fill="red" opacity="0.5" />
<path d="M10 10 L 190 10" stroke-width="4" stroke="black" mask="url(#mask)" />
</svg>

SVG Circles - Animate Segments? (Doughnut Chart)

<svg width="210px" height="210px" viewBox="0 0 210 210" class="donut">
<circle class="donut-ring" cx="105" cy="105" r="95" fill="transparent" stroke="black" stroke-width="20"></circle>
<circle class="donut-segment" cx="105" cy="105" r="95" fill="transparent" stroke="#1a1a1a" stroke-width="20" stroke-dasharray="149.22565104551518 447.67695313654554" stroke-dashoffset="150"></circle>
<circle class="donut-segment" cx="105" cy="105" r="95" fill="transparent" stroke="#333333" stroke-width="20" stroke-dasharray="149.22565104551518 447.67695313654554" stroke-dashoffset="597.6769531365455"></circle>
<circle class="donut-segment" cx="105" cy="105" r="95" fill="transparent" stroke="#434343" stroke-width="20" stroke-dasharray="298.45130209103036 298.45130209103036" stroke-dashoffset="448.45130209103036"></circle>
</svg>
Basically my problem is iv'e created a dynamic doughnut chart where the segments are automatically generated (class="donut-segment"). I need to make all the segments animate simultaneously from their start point to their end point. Iv'e done some pretty extensive searching but i can't find a example quite like mine. Any help at all would be greatly appreciated or just a point in the right direction.
I need the end result to have this effect
https://codepen.io/ksksoft/pen/xsnmp
Solved, Transitioned Stroke-DashArray from "0 and 600" into their proper values, and also transitioned the strokeDashOffset from 0 to its proper value

SVG absolute line marker size

I've got a bunch of SVGs representing a trip using a polyline.
I'm using non-scaling-stroke vector effect to make sure that each of the polylines is rendered with an absolute width. So when the viewBox's dimension changes, the width of the polyline does not.
At the ends of the mentioned polylines, I wanted to put markers. I would like to have the markers absolutely sized as well. I thought this should be easy by setting the marker units to strokeWidth.
In contrast to what happens with the stroke of the polyline, the size of the markers at the end of a polyline, does change along with the size of the viewBox.
Below I've included an example in where I used simple circles for markers.
<svg preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg" viewBox="131890.80533333332 85178.93015415945 198.25991111111944 205.10300513348193">
<defs>
<marker id="end" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
<marker id="start" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
</defs>
<g>
<polyline fill="none" marker-end="url(#end)" marker-start="url(#start)" stroke="#000" stroke-linejoin="round" stroke-width="3" vector-effect="non-scaling-stroke" points="132089.06524444444, 85384.03315929293 131890.80533333332, 85178.93015415945">
</polyline>
</g>
</svg>
<svg preserveAspectRatio="xMidYMid" xmlns="http://www.w3.org/2000/svg" viewBox="132038.56071111112 85364.68902323228 50.557866666669725 19.330493533576373">
<defs>
<marker id="end" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
<marker id="start" markerHeight="4" markerUnits="strokeWidth" markerWidth="4" orient="0deg" refX="8" refY="16" viewBox="0 0 16 20">
<circle cx="8" cy="16" fill="#000" r="4"></circle>
</marker>
</defs>
<g>
<polyline fill="none" marker-end="url(#end)" marker-start="url(#start)" stroke="#000" stroke-linejoin="round" stroke-width="3" vector-effect="non-scaling-stroke" points="132038.56071111112, 85364.68902323228 132089.1185777778, 85384.01951676585">
</polyline>
</g>
</svg>
See also: https://jsfiddle.net/u4bmupza/3/
Am I missing some SVG attributes or should I calculate the size of the markers manually?
vector-effect="non-scaling-stroke was first introduced in SVG 1.2 Tiny. That was a subset of SVG intended for for mobile devices with limited power. SVG 1.2 didn't have markers, so this issue wasn't a problem.
vector-effect was about the only thing from SVG 1.2 Tiny that browsers ended up implementing. Unfortunately, this problem was obviously missed at that time, and I guess no one has bothered to report it as a bug. Though I've seen it asked about on S.O. previously.
The good news is that the SVG2 spec specifically notes it as something that should be addressed, though that doesn't help you now.

changing the start point in svg circle

I want to use the below SVG but the the fill starts from 3 O clock.
I want the fill to start from 12 O clock.
Please give suggestions on how to achieve that.
<svg id="svg" width="200" height="200" viewPort="0 0 100 100" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48"
stroke-dashoffset="0"></circle>
<text id="percentile" x="50%" y="50%" text-anchor="middle" stroke="#191919"
stroke-width="1px" font-size="35" dy="0.3em">{{pct}}</text>
<!--<text id="percentile" x="50%" y="50%" text-anchor="middle" stroke="#191919"
stroke-width="1px" font-size="20" dy="-0.3em">Percentile</text>-->
<circle id="bar" r="90" cx="100" cy="100" fill="transparent"
stroke-dasharray="565.48" stroke-dashoffset="0"></circle>
</svg>
I presume you are using the image to show some kind of progress, i.e. it starts at completely gray, then gradually fills up with yellow starting from 3 o'clock as the proportion goes from 0 to 1 (or 0% to 100%), and the image shows what it looks like at 50%. You want to shift the start from 3 o'clock to 12 o'clock.
Update: The original solution I posted here suggested that you could solve this problem by using stroke-dashoffset. However, that solution was incorrect: it only works for small percentages of the circle, and breaks down once you try to fill larger proportions of the circle. The updated solution below shows an implementation of the suggestion in the comments below the original question. It involves rotating the circle. The demo code below also addresses one of the concerns you expressed for this solution in your comments, i.e. the demo code shows that if the rotation is applied only to the circle, it should not affect text that is placed within the circle.
<svg id="svg" width="500" height="200">
<g transform="">
<circle r="90" cx="100" cy="100" fill="none" stroke="gray" stroke-width="20"></circle>
<circle id="bar" r="90" cx="100" cy="100" fill="none" stroke="orange" stroke-width="20" stroke-dasharray="70.69 494.80" transform="rotate(0, 100, 100)"></circle>
<text x="35" y="100" font-family="Verdana" font-size="15">Text inside circle</text>
</g>
<g transform="translate(250)">
<circle r="90" cx="100" cy="100" fill="none" stroke="gray" stroke-width="20"></circle>
<circle id="bar" r="90" cx="100" cy="100" fill="none" stroke="orange" stroke-width="20" stroke-dasharray="70.69 494.80" transform="rotate(-90, 100, 100)"></circle>
<text x="35" y="100" font-family="Verdana" font-size="15">Text inside circle</text>
</g>
</svg>

Resources