SVG Path - render only a percent of the specified path - svg

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>

Related

How to color a region i.e the intersection of two object?

I want to color an intersection of two object, let's say a circle and a rectangle.
What I have tried so far is to defined a path bounding that region then add the fill attribute, but it seems too complicated.
Is there any other ways to do so?
Let me elabotare more one the problem:
<svg width="352" height="57" xmlns="http://www.w3.org/2000/svg">
<line y2="0.75" x2="103.95" y1="43.15" x1="42.35" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="50.35" x2="201.55" y1="0.75" x1="103.95" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="19.15" x2="239.95" y1="49.55" x1="201.55" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="55.95" x2="282.35" y1="18.35" x1="240.75" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
<line y2="37.55" x2="351.15" y1="31.95" x1="0.75" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
</svg>
I have a set of lines which the end of one is another end of the other.
I have another line intersecing these line.
How could I color the region i.e the triangles formed by these line?
You can use <clipPath> to clip the rect with the circle like so:
svg{width:100vh;}
<svg viewBox="50 50 200 100">
<clipPath id="clip">
<use xlink:href="#c" />
</clipPath>
<g fill="none" stroke="black" >
<circle id="c" cx="100" cy="100" r="30" />
<rect id="r" x="90" y="80" width="80" height="60" />
</g>
<use xlink:href="#r" clip-path="url(#clip)" fill="gold" />
</svg>
You could work with opacity to let the color of an overlapped element shine through like shown here:
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg">
<g style="stroke:#000; stroke-width:1">
<circle cx="50" cy="50" r="50" opacity="0.5" style="fill:red" />
<circle cx="100" cy="50" r="50" opacity="0.5" style="fill:yellow" />
<rect x="0" y="70" width="150" height="50" fill-opacity="0.5" style="fill:blue" />
</g>
</svg>
You can also specify the opacity levels separately for stroke and fill by using stroke-opacity and fill-opacity.
Edit:
Looking at your edited question again: when you are dealing with a succession of lines (and curves) you should use <path> elements instead of individual <line> elements. One of its advantages is that it comes with a fill behaviour that is almost exactly as you want it to be. However, to suit the requirements of your example you would need to find the positions where the straight line enters and leaves the zig-zag shape. These positions then define the points used for your filled path. The "original" (non-filled) path is then plotted over the filled path:
<svg width="352" height="57" xmlns="http://www.w3.org/2000/svg">
<path d="M56,33 103.95,0.75 201.55,50.35 239.95,19.15 260,36z
M0.75,31.95 351.15,37.55" style="fill:yellow" />
<path d="M42.35,43.15 103.95,0.75 201.55,50.35 239.95,19.15 282.35,55.95
M0.75,31.95 351.15,37.55" style="stroke:black;fill:none"/>
</svg>

Using different units in SVG

I have a simple SVG which I need to make responsive horizontally. The rectangle (and SVG) should expand to 100% of the width, but the circles should always have a diameter of 10px. The circles should also be distributed evenly on the horizontal axis (0%, 33%, 66%, 100%)
To get the ball rolling here is the basic SVG (no responsiveness yet..)
<svg height="10" xmlns="http://www.w3.org/2000/svg">
<g fill="#333" fill-rule="evenodd">
<rect y="3" width="245" height="4" rx="2"/>
<circle cx="5" cy="5" r="5"/>
<circle cx="80" cy="5" r="5"/>
<circle cx="160" cy="5" r="5"/>
<circle cx="240" cy="5" r="5"/>
</g>
</svg>
I've also setup a Codepen at https://codepen.io/anon/pen/GBNadz
Any tips on where to start? Is this possible with SVG?
You can use unit identifiers to specify coordinates and lengths. Writing cx="5" and cx="5px" is equivalent. What you cannot do is use the CSS calc() function; and there are some special attributes like viewBox and transform that look like they define length values, but actually do not. For your use case, where you need to shift the outer dots a bit to the middle, a combination of percentage coordinates and unitless transform="translate(...) does the trick.
<svg height="10" width="100%" xmlns="http://www.w3.org/2000/svg">
<g fill="#333" fill-rule="evenodd">
<rect y="3" width="100%" height="4" rx="2"/>
<circle cx="5" cy="5" r="5"/>
<circle cx="33%" cy="5" r="5"/>
<circle cx="66%" cy="5" r="5"/>
<circle cx="100%" cy="5" r="5" transform="translate(-5)"/>
</g>
</svg>

SVG Circle rendering bug in Safari when circles are stacked

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>

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