How to change the start point of svg animateMotion? - svg

I'm trying to change the start point of the animation which can be seen at https://codepen.io/anon/pen/vdmMOJ.
The problem is how can i make the blue circle start from its own position instead it snapping to another position and then the animation starts.
<svg width="500px" height="100%" viewBox="-500 -500 1500 1500">
<defs id="SvgjsDefs1225"></defs>
<path id="SvgjsPath1226" d="
M 397.0000000000009, -60
m -348, 0
a 348,348 0 1, 0 696,0
a 348,348 0 1, 0 -696,0
" fill="none" stroke="#c9e3ff" stroke-width="12"></path>
<g id="SvgjsG1234" class="circleFive">
<circle id="SvgjsCircle1243" r="34.6" cx="215.5" cy="231.6" fill="#022549" cursor="pointer"></circle>
<path id="SvgjsPath1244" d="M28,38.92A10.59,10.59,0,0,0,32.83,40c2,0,3.5-1.18,3.5-3s-1.64-3-4-3a10.83,10.83,0,0,0-4,.71l.5-12.46H41V27H33.73l-.12,3.29a6,6,0,0,1,1.33-.12c4.19,0,7.25,2.91,7.25,7.53S38.69,45,33.61,45A11.4,11.4,0,0,1,28,43.79Z" transform="matrix(1, 0, 0, 1, 181.136, 199.592)" cursor="pointer" fill="white"></path>
<animateMotion dur="4s" path="M181.50000762939544 -291.6000061035156M-166.49999237060456 -291.6000061035156A348 348 0 1 0 529.5000076293954 -291.6000061035156A348 348 0 1 0 -166.49999237060456 -291.6000061035156" begin="click" fill="freeze" calcMode="linear" keyTimes="0;1" keyPoints="1;0" repeatCount="1"></animateMotion>
</g>
</svg>

When using motion paths, it's always a lot easier if you make it start at (0,0). That way removes a lot of the semi-complicated arithimetic needed to compensate for the difference between the animated element and the motion path.
Let's start by pretending the dot is on the LHS (the 9 o'clock position). The motion path would be like this:
<svg width="500px" height="100%" viewBox="-500 -500 1500 1500">
<defs id="SvgjsDefs1225"></defs>
<path id="SvgjsPath1226" d="
M 397.0000000000009, -60
m -348, 0
a 348,348 0 1, 0 696,0
a 348,348 0 1, 0 -696,0
" fill="none" stroke="#c9e3ff" stroke-width="12"></path>
<g id="SvgjsG1234" class="circleFive">
<circle id="SvgjsCircle1243" r="34.6" cx="215.5" cy="231.6" fill="#022549" cursor="pointer"></circle>
<path id="SvgjsPath1244" d="M28,38.92A10.59,10.59,0,0,0,32.83,40c2,0,3.5-1.18,3.5-3s-1.64-3-4-3a10.83,10.83,0,0,0-4,.71l.5-12.46H41V27H33.73l-.12,3.29a6,6,0,0,1,1.33-.12c4.19,0,7.25,2.91,7.25,7.53S38.69,45,33.61,45A11.4,11.4,0,0,1,28,43.79Z" transform="matrix(1, 0, 0, 1, 181.136, 199.592)" cursor="pointer" fill="white"></path>
<animateMotion dur="4s"
path="M 0, 0
a 348,348 0 1, 0 696,0
a 348,348 0 1, 0 -696,0"
begin="click" fill="freeze" calcMode="linear" keyTimes="0;1" keyPoints="1;0" repeatCount="1"></animateMotion>
</g>
</svg>
But instead, you've positioned it at some other point on the circle. We need to work out the angle in order to calculate the new motion path.
The dot is at (215.5, 231.6). The centre of the blue circle is at (397, -60).
Therefore relative to the centre, the dot is at (-181.5, 291.6).
Now we know that, we also know that the opposite side of the motion path circle is at 181.5, -291.6. So we can update the motion path with that info. The values for the motion path arc (a) commands will be double those coord values (363 and 583.2).
<svg width="500px" height="100%" viewBox="-500 -500 1500 1500">
<defs id="SvgjsDefs1225"></defs>
<path id="SvgjsPath1226" d="
M 397.0000000000009, -60
m -348, 0
a 348,348 0 1, 0 696,0
a 348,348 0 1, 0 -696,0
" fill="none" stroke="#c9e3ff" stroke-width="12"></path>
<g id="SvgjsG1234" class="circleFive">
<circle id="SvgjsCircle1243" r="34.6" cx="215.5" cy="231.6" fill="#022549" cursor="pointer"></circle>
<path id="SvgjsPath1244" d="M28,38.92A10.59,10.59,0,0,0,32.83,40c2,0,3.5-1.18,3.5-3s-1.64-3-4-3a10.83,10.83,0,0,0-4,.71l.5-12.46H41V27H33.73l-.12,3.29a6,6,0,0,1,1.33-.12c4.19,0,7.25,2.91,7.25,7.53S38.69,45,33.61,45A11.4,11.4,0,0,1,28,43.79Z" transform="matrix(1, 0, 0, 1, 181.136, 199.592)" cursor="pointer" fill="white"></path>
<animateMotion dur="4s"
path="M 0, 0
a 348,348 0 1, 0 363, -583.2
a 348,348 0 1, 0 -363, 583.2"
begin="click" fill="freeze" calcMode="linear" keyTimes="0;1" keyPoints="1;0" repeatCount="1"></animateMotion>
</g>
</svg>
But uh oh. The motion path is wonky now. It bulges. What caused that? The answer is that the coords we used for the dot (-181.5, 291.6) are not exactly on the circle. We can check by calculating what radius those values would correspond to.
r = sqrt(-181.5^2 + 291.6^)
= 343.47
Which is different from the radius of our circle (348). We can fix that by scaling our coordinate values up by the ratio (348 / 343.47)
The new coordinates are now (183.9, 295.4). If we use those values, instead, to update our motion math, we get:
<svg width="500px" height="100%" viewBox="-500 -500 1500 1500">
<defs id="SvgjsDefs1225"></defs>
<path id="SvgjsPath1226" d="
M 397.0000000000009, -60
m -348, 0
a 348,348 0 1, 0 696,0
a 348,348 0 1, 0 -696,0
" fill="none" stroke="#c9e3ff" stroke-width="12"></path>
<g id="SvgjsG1234" class="circleFive">
<circle id="SvgjsCircle1243" r="34.6" cx="215.5" cy="231.6" fill="#022549" cursor="pointer"></circle>
<path id="SvgjsPath1244" d="M28,38.92A10.59,10.59,0,0,0,32.83,40c2,0,3.5-1.18,3.5-3s-1.64-3-4-3a10.83,10.83,0,0,0-4,.71l.5-12.46H41V27H33.73l-.12,3.29a6,6,0,0,1,1.33-.12c4.19,0,7.25,2.91,7.25,7.53S38.69,45,33.61,45A11.4,11.4,0,0,1,28,43.79Z" transform="matrix(1, 0, 0, 1, 181.136, 199.592)" cursor="pointer" fill="white"></path>
<animateMotion dur="4s"
path="M 0, 0
a 348,348 0 1, 0 367.8, -590.8
a 348,348 0 1, 0 -367.8, 590.8"
begin="click" fill="freeze" calcMode="linear" keyTimes="0;1" keyPoints="1;0" repeatCount="1"></animateMotion>
</g>
</svg>

Svg and positions
The circle has its own starting position:
Where its coordinates(starting point) are defined by cx and cy
circle cx="215.5" cy="231.6"
But this is manipulated by the transform property:
transform="matrix(1, 0, 0, 1, 181.136, 199.592)"
The path that the animation starts on is defined by animateMotion's path.
The two first coordinated in the path is where the path starts:
animateMotion Path="M181.50000762939544 -291.6000061035156 [...]"
Sure, but how do i fix this?
Lets start with removing the transform on the circle. Auto generating programs love using those and it makes the coordinates impossible for a human to calculate.
Then change the starting position of the circle so it matches the animateMations starting position on the path.

Related

SVG is not scaling when adding sizing to it with CSS

I'm trying to resize an svg element using css by setting width and height to a smaller value than the inline one, but it is not scaling, the end result is an empty square, the thing is that the <svg> element itself changes sizes, but the <path>'s inside it do not, this is shown by inspecting them in the browser's devtools.
The svg element is imported as a react component using SVGR, the styling is set using styled-components.
Before writing this post I read some possible solutions online, none worked (removing inline width and height, adding viewbox to same values as width and height etc...), any clue?
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg">
<title/>
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
</g>
<g>
<title>Layer 1</title>
<path id="svg_1" fill="#fff133" d="m276.6,127.6a148.4,148.4 0 0 0 -114.46,242.86a148.49,148.49 0 0 0 164.33,16.54a150.66,150.66 0 0 1 -15.94,-16.51a148.38,148.38 0 0 1 9.79,-236.29a148.18,148.18 0 0 0 -43.72,-6.6z"/>
<path id="svg_2" stroke-width="20" stroke-linejoin="round" stroke-linecap="round" stroke="#455a64" fill="none" d="m116.5,207c-0.37,1.05 -0.73,2.11 -1.07,3.17"/>
<path id="svg_3" stroke-width="20" stroke-linejoin="round" stroke-linecap="round" stroke="#455a64" fill="none" d="m109.77,234.43a148.43,148.43 0 0 0 221,150.11a148.44,148.44 0 0 1 0,-257.08a148.46,148.46 0 0 0 -204,56.62"/>
</g>
</svg>

Using clip-path to remove 37px from 4 svg lines in the circle

What I'm trying to do is remove the svg lines in the middle of the circle.
How would this be done using clip-path?
Taking this:
https://i.imgur.com/DGX3Yji.png
And turning it to this:
https://i.imgur.com/gg4XFUq.png
Code:
https://jsfiddle.net/5r1dg4hx/3/
https://i.imgur.com/SGzGzaE.png
<svg class="stack" aria-hidden="true" width="260" height="194" viewbox="0 0 260 194">
<line stroke="teal" x1="131" y1="40" x2="130" y2="149"></line>
<line stroke="dodgerblue" x1="91" y1="133" x2="170" y2="58"></line>
<line stroke="purple" x1="77" y1="95" x2="185" y2="96"></line>
<line stroke="green" x1="169" y1="135" x2="93" y2="56"></line>
<circle class="outer" cx="131" cy="95" r="55"></circle>
<circle cx="130" cy="95.40" r="20.8"></circle>
You will need to draw a path with a hole for the clipping path:
<path d="M0,0 0,194 260,194 260,0 0,0
M150.8, 95.40A20.8, 20.8 0 0 1 109.2 95.40A20.8, 20.8 0 0 1 150.8, 95.40" />
The first part of the path (M0,0 0,194 260,194 260,0 0,0) is drawing a rectangle as big as the svg canvas. The second part (M150.8, 95.40A20.8, 20.8 0 0 1 109.2 95.40A20.8, 20.8 0 0 1 150.8, 95.40) is drawing a circle as big as the inner circle and in the same position.
Next you wrap the lines in a group and you clip the group.
svg{border:1px solid;}
circle{fill:none;stroke:black}
<svg class="stack" aria-hidden="true" width="260" height="194" viewbox="0 0 260 194">
<defs>
<clipPath id="clip">
<path d="M0,0 0,194
260,194 260,0 0,0 M150.8, 95.40A20.8, 20.8 0 0 1 109.2 95.40A20.8, 20.8 0 0 1 150.8, 95.40" />
</clipPath>
</defs>
<g clip-path="url(#clip)">
<line stroke="teal" x1="131" y1="40" x2="130" y2="149"></line>
<line stroke="dodgerblue" x1="91" y1="133" x2="170" y2="58"></line>
<line stroke="purple" x1="77" y1="95" x2="185" y2="96"></line>
<line stroke="green" x1="169" y1="135" x2="93" y2="56"></line>
</g>
<circle class="outer" cx="131" cy="95" r="55"></circle>
<circle cx="130" cy="95.40" r="20.8"></circle>
</svg>

Merging svg line and path into one path element

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>

How can I draw an ellipse as a path in svg?

I have to draw the solar system in a svg file, so I have to draw an ellipse in a svg as a path.
Someone could help me?
<!--Venus-->
<path id="venere" fill="none" stroke="white" stroke-width="2"
d="
M 650, 300
m -75, 0
a 75,75 0 1,0 150,0
a 75,75 0 1,0 -150,0
"
/>
<circle cx="100" cy="100" r="10" fill="green">
<animateMotion dur="5s" repeatCount="indefinite">
<mpath xlink:href="#venere"/>
</animateMotion>
</circle>
This create a circle, but I need an ellipse
You're getting a circle because the rx and ry values of the elliptical arc are both 75 (they are the two values immediately after the a command). If they were different you'd get an ellipse.
Here is a solution with simplified code, where the ellipse is draw in a single stroke, as opposed to being draw as two halves.
<path id="venere" fill="none" stroke="white" stroke-width="2"
d="M 650, 150 a 75,150 0 1,0 1,0"/>
In addition to the previous solution, if you add closepath command z to the end, you will get rid of missing pixel point. So it should be:
<path id="venere" fill="none" stroke="white" stroke-width="2"
d="M 650, 150 a 75,150 0 1,0 1,0 z"/>

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