Merging svg line and path into one path element - svg

I have one line element and one path element. I would like to merge them into one path element, but I have no experience with svg drawing.
How can I marge these two svg elements into one viewport/viewBox where "top point" of vert-line-2 is joined with y2 of vert-line-1.
.svg-wrapper{
width: 55px;
height: 55px;
font-size: 0;
}
.play-vert-line{
width: 100%;
}
.line-join{
position: relative;
left: 25px;
fill: none;
}
<div class="svg-wrapper">
<svg class="play-vert-line" height="17">
<defs>
<linearGradient id="vert-gradient" gradientUnits="userSpaceOnUse" y1="0%" y2="100%" x1="0" x2="0">
<stop stop-color="#3A9AFC"></stop>
<stop stop-color="#3c3c3c"></stop>
</linearGradient>
</defs>
<line id="vert-line-1" x1="50%" y1="0" x2="50%" y2="17" stroke="url(#vert-gradient)" stroke-width="2"></line>
</svg>
<svg class="line-join" height="15" width="15" viewBox="-1.3 0 15 15">
<defs>
<linearGradient id="vert-join-gradient" gradientUnits="userSpaceOnUse" y2="100%" x1="0" x2="0">
<stop stop-color="#3A9AFC"></stop>
<stop stop-color="#3c3c3c"></stop>
</linearGradient>
</defs>
<path id="vert-line-2" d="M 11, 10 C 5, 10, 1, 6, 1, 0" stroke="url(#vert-join-gradient)" stroke-width="2"></path>
</svg>
</div>

Let's walk you through this step by step.
Resolve linear gradients
The linear gradients defining the strokes both have <stop> child elements that lack offset attributes. Thus, they are all at the default value 0. The net effet is that, since all parts of the stroke are on the positive side of the gradient vector origin, the strokes have a uniform color of #3c3c3c. fill:none has been moved to an attribute.
<div class="svg-wrapper">
<svg class="play-vert-line" height="17">
<line id="vert-line-1" x1="50%" y1="0" x2="50%" y2="17" stroke="#3c3c3c" stroke-width="2"></line>
</svg>
<svg class="line-join" height="15" width="15" viewBox="-1.3 0 15 15">
<path id="vert-line-2" d="M 11, 10 C 5, 10, 1, 6, 1, 0"
stroke="#3c3c3c" stroke-width="2" fill="none"></path>
</svg>
</svg>
Move the two <svg> elements in one parent <svg>
div.svg-wrapper is exchanged for a svg element of the same size (55*55), and the two svgs are placed inside, positioned and sized as defined by the stylesheet. All Percentage values at this point are exchanged for absolute values.
It should be noted that formally, the inner svgs must define a overflow="hidden" attribute (implicitely defining a clip-path). It can be safely left off, since the grafical elements do not overflow their viewport.
<svg class="svg-wrapper" width="55" height="55" viewBox="0 0 55 55">
<svg class="play-vert-line" height="17" width="55">
<line id="vert-line-1" x1="27.5" y1="0" x2="27.5" y2="17" stroke="#3c3c3c" stroke-width="2"></line>
</svg>
<svg class="line-join" x="25" y="17" height="15" width="15" viewBox="-1.3 0 15 15">
<path id="vert-line-2" d="M 11, 10 C 5, 10, 1, 6, 1, 0"
stroke="#3c3c3c" stroke-width="2" fill="none"></path>
</svg>
</svg>
Compute the equivalent transform of the child <svg>s
At this point, the inner svgs have their own viewports and own coordinate system. The transformation to the parent viewport can be computed following this algorithm. Then, the <svg> elements can be exchanged for <g> elements.
If there had been overflow, a clip-path attribute would have been necessary.
<svg class="svg-wrapper" width="55" height="55" viewBox="0 0 55 55">
<g class="play-vert-line">
<line id="vert-line-1" x1="27.5" y1="0" x2="27.5" y2="17" stroke="#3c3c3c" stroke-width="2"></line>
</g>
<g class="line-join" transform="translate(26.3, 17)">
<path id="vert-line-2" d="M 11, 10 C 5, 10, 1, 6, 1, 0"
stroke="#3c3c3c" stroke-width="2" fill="none"></path>
</g>
</svg>
Apply the transform and convert line to path
Effectively, only the <path> element has transform applied. The group elements can be removed, and in the d attribute, every coordinate has to be recomputed: x' = x + dx, y' = y + dy.
To transform <line> to <path>, the d attribute must be written using the start and end coordinates of the line:
d="M <x1> <y1> L <x2> <y2>"
(The L command can be left off, as it is implied.)
<svg class="svg-wrapper" width="55" height="55" viewBox="0 0 55 55">
<path id="vert-line-1" d="M 27.5 0 27.5 17" stroke="#3c3c3c" stroke-width="2"></path>
<path id="vert-line-2" d="M 36.3, 27 C 31.3, 27, 27.3, 23, 27.3, 17"
stroke="#3c3c3c" stroke-width="2" fill="none"></path>
</svg>
Merging and joing the two path elements
Since the two path elements are now expressed in the same coordinate systems and have the same presentation attributes (don't forget fill="none"), the d attributes can now simply be concatenated:
d="M 27.5 0 27.5 17 M 36.3, 27 C 31.3, 27, 27.3, 23, 27.3, 17"
Joining them has one further complication. The first subpath starts at the upper end, but the second starts on the lower. To join them in a top-to-bottom direction, the direction of the second subpath has to be reversed. In the special case of a C command, all coordinates can simply be reversed in order. For other commands (especially relative path commands), this could be more complicated.
d="M 27.5 0 27.5 17 M 27.3, 17 C 27.3, 23, 31.3, 27, 36.3, 27"
You did't say how to join the two subpaths. Let's assume a straight line.
<svg class="svg-wrapper" width="55" height="55" viewBox="0 0 55 55">
<path id="vert-line" d="M 27.5 0 27.5 17 L 27.3, 17 C 27.3, 23, 31.3, 27, 36.3, 27"
stroke="#3c3c3c" stroke-width="2" fill="none"></path>
</svg>

Related

CSS transform on SVG text element not working in Safari

Trying to position a battery indicator within a parent SVG.The SVG <svg viewBox="0 0 24 24"> element has a path for the battery and a text element showing the percentage.Its being positioned with a couple of css transforms and text attributes.The text is correctly positioned when opening in chrome/firefox but goes offshoot in safari.
<text
text-anchor="middle"
dominant-baseline="middle"
style="transform:translate(50%,98%) scale(.2);
font:700 13px sans-serif;fill:#deba78"
>24.2%</text>
Codepen https://codepen.io/niwsa/pen/rNNBKEg?editors=1000
Wrap the <text> element into a <g> element and apply the CSS transform on that, it happens to work currently even in Safari. This way, it's still CSS, so CSS transitions, animations, custom properties, selector based styling, continuous font scaling etc. work.
Instead of translating the text you may give the text some attributes like x and y. Instead of scaling the text you may change the font-size.
For the path you may choose svg transforms like this:
body {
width: 200px;
}
.bg {
fill: #beeb1b;
}
.cap {
fill: #aaa8a9;
}
.trunk {
fill: #231f20;
}
<svg xmlns="http://www.w3.org/2000/svg" tabindex="0" viewBox="0 0 351.33 722" aria-labelledby="bottletitle bottledesc" role="img">
<title id="bottletitle">
Bottle
</title>
<desc id="bottledesc">
Bottle with battery indicator inside
</desc>
<g data-name="Layer 4" class="bg">
<rect width="351.33" height="722" rx="23.33" ry="23.33"/>
</g>
<g data-name="Layer 3" class="cap">
<rect x="146.81" y="60.9" width="57.71" height="73.67"/>
</g>
<g data-name="Layer 2" class="trunk">
<path d="M173,153.25h57.75V223s1.08,25.33,30.41,56c27.06,28.29,35.34,60.33,35,71.33-.21,7,0,324.67,0,324.67s-3.33,7.33-9.33,7.33H117.12s-9-.33-9-6.66v-325s-.33-33,30.34-67c0,0,34.33-32.67,34.33-60.67S173.33,153.63,173,153.25Z" transform="translate(-26.46 -18.67)"/>
</g>
<svg viewBox="0 0 24 24">
<defs>
<linearGradient id="lg" x1="0.5" y1="1" x2="0.5" y2="0">
<stop offset="0%" stop-opacity="1" stop-color="#2ecc71"/>
<stop offset="24.2%" stop-opacity="1" stop-color="#2ecc71"/>
<stop offset="24.2%" stop-opacity="0" stop-color="#2ecc71"/>
<stop offset="100%" stop-opacity="0" stop-color="#2ecc71"/>
<animate attributeName="y2" from="1" to="0" dur="500ms" repeatCount="2s" fill="freeze"/>
</linearGradient>
</defs>
<path fill="url(#lg)" d="M15.67 4H14V2h-4v2H8.33C7.6 4 7 4.6 7 5.33v15.33C7 21.4 7.6 22 8.33 22h7.33c.74 0 1.34-.6 1.34-1.33V5.33C17 4.6 16.4 4 15.67 4z" stroke="#fff" transform="scale(.5,.5) rotate(90,12 12) translate(45,-12)"/>
<text text-anchor="middle" dominant-baseline="middle" style="font:700 2.5px sans-serif;fill:#deba78" x="12" y="23">
24.2%
</text>
</svg>
</svg>

How to fill a color to a donut SVG path by percentage by starting from left side?

I have a donut SVG path. It must start from left side to fill a color and must end at the right side and it must fill by percentage.
See this:
(source: eksiup.com)
I tried to achieve this rotating clipPath like this:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 92 100">
<defs>
<clipPath id="knobMask">
<path style="fill: #ff554e;" d="
M 17.72, 74.28
a 40, 40,0, 1, 1, 56.56, 0
l 4.25, 4.25
a 46, 46, 0, 1, 0-65.06 ,0 Z" />
</clipPath>
</defs>
<g>
<g id="Layer_1" data-name="Layer 1">
<g id="Path_34688-6" data-name="Path 34688-6">
/>
</g>
<path d="M17.72,74.28a40,40,0,1,1,56.56,0l4.25,4.25a46,46,0,1,0-65.06,0Z" />
<g clip-path="url(#knobMask)" transform="rotate(-45,45,45)">
<path style="fill: #ff554e;" d="M 17.72,74.28
a 40,40,0,1,1,56.56,0
l 4.25,4.25
a 46,46,0,1,0-65.06,0Z" />
</g>
</g>
But no luck. How can I fill a color to this path starting from left side by percentage?
Here is the JS Fiddle
Basically I want this:
(source: eksiup.com)
As I've commented there is a simpler way to achieve this. You simplify the path to d="M 17.72,74.28a 40,40,0,1,1,56.56,0" and use fill="none" and stroke="10" for example. For the red path you are using the same d as before and you use stroke-dasharray to reduce the apparent length of the path. If you are using this d the total length of the path is 188.53 so you can use 188.53 / 2 = 94.265 : stroke-dasharray=94.265"
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 92 100" width="200">
<defs>
<path id="base" fill="none" stroke-width="10" d="
M 17.72, 74.28
a 40, 40,0, 1, 1, 56.56, 0" />
</defs>
<use xlink:href="#base" stroke="black" />
<use xlink:href="#base" stroke="#ff554e" stroke-dasharray="94.265" />
</svg>
In the next example I'm using javascript to calculate the value of the dash and the value of the gap. Please use the input type range to change the values.
let totallength=base.getTotalLength();
itr.addEventListener("input",()=>{
let newSDA = (Number(itr.value)*totallength) / 100;
let gap = totallength - newSDA
red.setAttributeNS(null,"stroke-dasharray",`${newSDA} ${gap}`)
})
svg{border:1px solid}
<p><input id="itr" type="range" /></p>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 92 100" width="200">
<defs>
<path id="base" fill="none" stroke-width="10" d="
M 17.72, 74.28
a 40, 40,0, 1, 1, 56.56, 0" />
</defs>
<use xlink:href="#base" stroke="black" />
<use xlink:href="#base" id="red" stroke="#ff554e" stroke-dasharray="94.265" />
</svg>

SVG file only works in certain places

I have a very simple svg file, a book shelf, which I extracted from a public domain image in Inkscape.
My intention is to use an SVG Renderer within WinForms to produce bitmaps of variable widths from it.
It works fine within Inkscape but doesn't want to display in IE or Chrome or the DevExpress Svg renderer or a public domain SVG Viewer.
What should I change in the XML to make it work outside Inkscape?
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg11134"
version="1.1"
viewBox="0 0 100 9.9999993"
height="10mm"
width="100mm">
<defs
id="defs11128">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath304">
<path
d="M 4410,3710 H 90 v -180 h 4320 z"
id="path302" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath320">
<path
d="M 360,3890 90,3710 h 4320 l -270,180 z"
id="path318" />
</clipPath>
<linearGradient
x1="0"
y1="0"
x2="1"
y2="0"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-7.9e-6,180,180,7.9e-6,2250,3710)"
spreadMethod="pad"
id="linearGradient326">
<stop
style="stop-opacity:1;stop-color:#ebecea"
offset="0"
id="stop322" />
<stop
style="stop-opacity:1;stop-color:#d6d7d5"
offset="1"
id="stop324" />
</linearGradient>
</defs>
<g
transform="translate(13.396613,-100.26342)"
id="layer1">
<g
id="g9848"
transform="matrix(-0.62409419,0,0,0.93340311,-15.02437,93.369296)">
<g
transform="matrix(0.02909742,0,0,-0.02975968,4.8287909,123.15116)"
id="g9840">
<g
id="g9838"
>
<path
d="M 4410,3710 H 90 v -180 h 4320 v 180"
style="fill:#d6d7d5;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path9836" />
</g>
</g>
<g
transform="matrix(0.02909742,0,0,-0.02975968,4.8287909,123.15116)"
id="g9846">
<g
id="g9844"
>
<path
d="M 360,3890 90,3710 h 4320 l -270,180 H 360"
style="fill:url(#linearGradient326);fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path9842" />
</g>
</g>
</g>
</g>
</svg>
The grafics were outside the viewBox limits (outside the area displayed).
Inkscape tends to produce a lot of crud that makes maintaining and changing a SVG outside the visual editor almost impossible. The following is the same image reduced to the bare essentials, including some changes:
I have removed the width/height values for the SVG. Giving dimensions in real-live values (here: mm) is not helpfull in most cases. and you said you wanted to produce bitmaps of variable size anyway. This amounts to overwriting these values, so you can leave them off in the source file.
The grafic elements have been moved inside the viewBox and all transformations resolved.
The grafics would not have filled the full viewBox width. I have widened them to a 100:10 ratio.
If you want to change the height/width ratio, you can set an attribute preserveAspectRatio="none" on the <svg> element and the set arbitrary output dimensions in the export program. The grafics will then scale non-uniformly and always fill the viewport.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 10">
<defs>
<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="50" y1="5" x2="50" y2="0">
<stop offset="0" stop-color="#ebecea" />
<stop offset="1" stop-color="#d6d7d5" />
</linearGradient>
</defs>
<path d="M 100,5 V 10 H 0 V 5 Z" fill="#d6d7d5" />
<path d="M 100,5 L 95,0 H 5 L 0,5 Z" fill="url(#grad)" />
</svg>

How to set up gulp-svgmin in the safest way?

does anyone know how to use gulp-svgmin?
I tested it with a simple svg file, but the output removed the clipPath and mask tags
input:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="hex-v" clipPathUnits="objectBoundingBox"><!--objectBoundingBox is important, so that the points becomes percentage values inside the element clipped-->
<polygon points=".25 0, .75 0, 1 .5, .75 1, .25 1, 0 .5"/>
</clipPath>
<clipPath id="hex-h" clipPathUnits="objectBoundingBox"><!--objectBoundingBox is important, so that the points becomes percentage values inside the element clipped-->
<polygon points=".5 0, 1 .25, 1 .75, .5 1, 0 .75, 0 .25"/>
</clipPath>
<radialGradient id="gradient" r="1">
<stop stop-color="white" offset="50%"/>
<stop stop-color="black" offset="70%"/>
</radialGradient>
<mask id="masking" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
<rect width="1" height="1" fill="url(#gradient)"/>
</mask>
</defs>
</svg>
output:
<svg xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="a" r="1"><stop stop-color="#fff" offset="50%"/><stop offset="70%"/></radialGradient></defs></svg>
So I assume it's because there are some options I should set for the svgmin?
I want svgmin to minify the files in the safest way possible, so I don't get unexpected results.
Or is there a different gulp plugin I can use?
Thanks

SVG Position text relative to parent <g>

I need to place text relative to the parent <g>.
Currently, I have a path and a text element wrapped in a <g>. But all the text coordinates are relative to the outer most <g>.
<svg width="1000" height="550">
<g transform="translate(500,275)" stroke-width="2" stroke="black">
<g>
<path d="M1.6838893488276108e-14,-275A275,275 0 0,1 238.15698604072065,137.49999999999994L0,0Z" id="path_0" style="fill: rgb(17, 17, 17);"></path>
<text transform="translate(100, 100)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
<g>
<path d="M238.15698604072065,137.49999999999994A275,275 0 0,1 -238.1569860407206,137.50000000000009L0,0Z" id="path_1" style="fill: rgb(34, 34, 34);"></path>
<text transform="translate(100, 100)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
<g>
<path d="M-238.1569860407206,137.50000000000009A275,275 0 0,1 -5.051668046482832e-14,-275L0,0Z" id="path_2" style="fill: rgb(51, 51, 51);"></path>
<text transform="translate(100, 100)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
</g>
</svg>
It's hard to see which part of this you're having trouble with, but I'll explain as best I can.
Your SVG picture is 1000 pixels wide and 550 pixels tall:
<svg width="1000" height="550">
The top level node inside this SVG is a <g> node that moves the origin of the coordinate system from the top left corner to (500,275) (i.e., the middle of the drawing area; Y coordinates increase from top to bottom in SVGs)
<g transform="translate(500,275)" ... >
All the children of this top-level node will therefore use this transformed coordinate system. You have added additional <g> nodes as children of this top-level node, but they don't really do anything in this instance because they contain no attributes:
<g>
As a result, the <path> nodes will still be using the same transformed coordinate system that was set up by the top-level <g>. These all produce circular sectors with an apex at (0,0). And since (0,0) corresponds to the middle of the drawing area in this transformed coordinate system, that is where they end up:
<path d="M0-275A275 275 0 0 1 238 137.5L0 0Z" style="fill: rgb(17, 17, 17);"></path>
<path d="M238 137.5A275 275 0 0 1-238 137.5L0 0Z" style="fill: rgb(34, 34, 34);"></path>
<path d="M-238 137.5A275 275 0 0 1 0-275L0 0Z" style="fill: rgb(51, 51, 51);"></path>
Your <text> nodes are also drawn in this coordinate system, but offset by (100,100) because you added a transform attribute to shift them 100 pixels down and 100 pixels to the right:
<text transform="translate(100, 100)" ... >text</text>
So the end result is that all three of your text nodes are drawn at coordinates of (600,375) relative to the top left corner of the drawing area. If you want the text to appear somewhere else, you'll have specify a different offset. For example:
<text transform="translate(120,-80)" ... >text</text>
<text transform="translate(0,160)" ... >text</text>
<text transform="translate(-120,-80)" ... >text</text>
<svg width="1000" height="550">
<g transform="translate(500,275)" stroke-width="2" stroke="black">
<g>
<path d="M0-275A275 275 0 0 1 238 137.5L0 0Z" style="fill: rgb(17, 17, 17);"></path>
<text transform="translate(120,-80)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
<g>
<path d="M238 137.5A275 275 0 0 1-238 137.5L0 0Z" style="fill: rgb(34, 34, 34);"></path>
<text transform="translate(0,160)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
<g>
<path d="M-238 137.5A275 275 0 0 1 0-275L0 0Z" style="fill: rgb(51, 51, 51);"></path>
<text transform="translate(-120,-80)" class="tooltipText" stroke-width="0" fill="white" style="text-anchor: middle;">text</text>
</g>
</g>
</svg>

Resources