SVG with text and graphic - how to style with CSS - css

Trying to convert my auto-generated charts from PNG to SVG. Chart and axis are easy, but I don't know how to implement the legend in a nice + good-looking way.
My results so far are looking like this:
https://jsfiddle.net/m4zuqk12/ , an (absolute) minimum example would be:
graph.svg:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<?xml-stylesheet href="graph.css" type="text/css"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="768" version="1.1">
<g class="trajectory trajectory-b">
<path d="M 142 307 L 333 307 L 333 242 L 353 242" fill="none"/>
<path d="M 100 718 m 0 11.5 l 50 0"/>
<text x="160" y="733" class="legend">Graph 2</text>
</g>
<g class="trajectory trajectory-c">
<path d="M 177 357 L 325 357 L 325 450 L 431 450" fill="none"/>
<path d="M 562 718 m 0 11.5 l 50 0"/>
<text x="622" y="733" class="legend">Graph 3</text>
</g>
<g class="trajectory trajectory-d">
<path d="M 232 542 L 332 542 L 332 507 L 432 507" fill="none"/>
<path d="M 100 718 m 0 34.5 l 50 0"/>
<text x="160" class="legend"><tspan dy="1.2em">Graph 4</tspan></text>
</g>
<g class="legend">
<path class="trajectory-b" d="M 100 668 m 0 11.5 l 50 0"/>
<path class="trajectory-c" d="M 562 668 m 0 11.5 l 50 0"/>
<path class="trajectory-d" d="M 100 668 m 0 34.5 l 50 0"/>
<text x="100" y="668">
<tspan class="trajectory-b" x="160" dy="1.2em">Graph 2</tspan>
<tspan class="trajectory-c" x="622" dy="0">Graph 3</tspan>
<tspan class="trajectory-d" x="160" dy="1.2em">Graph 4</tspan>
</text>
</g>
</svg>
graph.css:
g.axis {
stroke: black;
}
.legend .trajectory-b,
g.trajectory-b {
stroke: red;
fill: red;
}
.legend .trajectory-c,
g.trajectory-c {
stroke: green;
fill: green;
}
.legend .trajectory-d,
g.trajectory-d {
stroke: blue;
fill: blue;
}
g.trajectory:hover path {
stroke-width: 5px;
}
Note there are two legends: The upper one has better text placement but no hover-effects, the lower one has hover-effects, but worse placement.
Upper legend: seems to be a proper way to write multi-line texts; but I do not see any way to access the graphs via CSS this way - is there?
Lower legend: the texts are written within the of the respective graph. This way, hover-effects via CSS are easy, but I have to guess the y-positions (which ist not possible in real life, because the styling is left to different CSS-designers). Is it possible to simulate the 1.2em-placement? I know that I'd have to wait vor SVG2.0 to use "100px+1.2em", but perhaps there is a way to work around this limitation?
Bonus problem: how to place the lines in front of the legend text? I can't see any way at all to do this properly.

Related

How to resize only 1 part of a SVG in order to fill the content

I understand that SVG actually doesn't have a content, but I am struglying with this problem and I can't solve during days.
I have a Figma (so I can get the SVG) with this design:
The problem is that "Your Collection" text needs to grow to the right in some situations, for example: When I translate the app to Spanish and I have to show: "Tu Coleccion", or some other languages with even longer texts.
In the Figma that shape is made by 2 shapes + an "UNION" rule from Figma:
body { background-color: #7fb6ff80;}
<svg width="188" height="71" viewBox="0 0 188 71" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_193_1631" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.3203 50.6719C68.8459 50.6719 65.7635 52.7341 63.7839 55.5894C57.4655 64.7034 46.9298 70.6719 35 70.6719C15.67 70.6719 0 55.0019 0 35.6719C0 16.342 15.67 0.671936 35 0.671936C46.9298 0.671936 57.4655 6.64051 63.7839 15.7544C65.7635 18.6098 68.8459 20.6719 72.3203 20.6719H173C181.284 20.6719 188 27.3877 188 35.6719C188 43.9562 181.284 50.6719 173 50.6719H72.3203Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.3203 50.6719C68.8459 50.6719 65.7635 52.7341 63.7839 55.5894C57.4655 64.7034 46.9298 70.6719 35 70.6719C15.67 70.6719 0 55.0019 0 35.6719C0 16.342 15.67 0.671936 35 0.671936C46.9298 0.671936 57.4655 6.64051 63.7839 15.7544C65.7635 18.6098 68.8459 20.6719 72.3203 20.6719H173C181.284 20.6719 188 27.3877 188 35.6719C188 43.9562 181.284 50.6719 173 50.6719H72.3203Z" fill="white"/>
<path d="M63.7839 15.7544L62.9621 16.3242L63.7839 15.7544ZM62.9621 55.0197C56.8225 63.8757 46.588 69.6719 35 69.6719V71.6719C47.2715 71.6719 58.1085 65.531 64.6057 56.1592L62.9621 55.0197ZM35 69.6719C16.2223 69.6719 1 54.4496 1 35.6719H-1C-1 55.5542 15.1177 71.6719 35 71.6719V69.6719ZM1 35.6719C1 16.8943 16.2223 1.67194 35 1.67194V-0.328064C15.1177 -0.328064 -1 15.7897 -1 35.6719H1ZM35 1.67194C46.588 1.67194 56.8225 7.4682 62.9621 16.3242L64.6057 15.1847C58.1085 5.81283 47.2715 -0.328064 35 -0.328064V1.67194ZM72.3203 21.6719H173V19.6719H72.3203V21.6719ZM173 21.6719C180.732 21.6719 187 27.9399 187 35.6719H189C189 26.8354 181.837 19.6719 173 19.6719V21.6719ZM187 35.6719C187 43.4039 180.732 49.6719 173 49.6719V51.6719C181.837 51.6719 189 44.5085 189 35.6719H187ZM173 49.6719H72.3203V51.6719H173V49.6719ZM62.9621 16.3242C65.0754 19.3724 68.4347 21.6719 72.3203 21.6719V19.6719C69.2571 19.6719 66.4515 17.8471 64.6057 15.1847L62.9621 16.3242ZM64.6057 56.1592C66.4515 53.4968 69.2571 51.6719 72.3203 51.6719V49.6719C68.4347 49.6719 65.0754 51.9714 62.9621 55.0197L64.6057 56.1592Z" fill="#F2F2F2" mask="url(#path-1-inside-1_193_1631)"/>
</svg>
Do you have any idea how could I change the width of that second part in order to fill the content, without changing the circle part?
Do you have any idea how to get this with CSS (without SVG)? The harder part with CSS is the curve that is in the union between the circle and the rectangle. The other part is simple.
I think it would be a good idea to have a "sliding doors" effect. So, here I made patterns for both the circle and the "text label". Switching between patterns with different sizes makes the text label resize.
This can be done dynamically if you look for the size of the text and then update the pattern based on that.
body { background-color: #7fb6ff80;}
svg {
display: block;
}
<svg width="0" height="0" viewBox="0 0 400 104" xmlns="http://www.w3.org/2000/svg">
<pattern id="circle" viewBox="0 0 104 104" width="100%" height="100%">
<path transform="translate(2 2)" stroke="black" stroke-width="2" fill="none"
d="M 102 30 Q 97 30 92 23 Q 76 0 50 0 A 10 10 90 0 0 50 100 Q 76 100 92 77 Q 97 70 102 70"/>
</pattern>
<pattern id="p1_120" viewBox="0 0 400 104" width="100%" height="100%">
<path transform="translate(120 2)" stroke="gray" stroke-width="2" fill="none"
d="M -200 30 H 0 A 1 1 0 0 1 0 70 H -200"/>
</pattern>
<pattern id="p1_140" viewBox="0 0 400 104" width="100%" height="100%">
<path transform="translate(140 2)" stroke="black" stroke-width="2" fill="none"
d="M -200 30 H 0 A 1 1 0 0 1 0 70 H -200"/>
</pattern>
<pattern id="p1_160" viewBox="0 0 400 104" width="100%" height="100%">
<path transform="translate(160 2)" stroke="black" stroke-width="2" fill="none"
d="M -200 30 H 0 A 1 1 0 0 1 0 70 H -200"/>
</pattern>
</svg>
<svg id="svg01" viewBox="0 0 400 104" xmlns="http://www.w3.org/2000/svg">
<rect x="104" width="400" height="104" fill="url(#p1_160)" />
<rect width="104" height="104" fill="url(#circle)"/>
<text x="110" y="53" dominant-baseline="middle" font-size="20"
font-family="sans-serif">Your Collection</text>
</svg>
<svg id="svg02" viewBox="0 0 400 104" xmlns="http://www.w3.org/2000/svg">
<rect x="104" width="400" height="104" fill="url(#p1_140)" />
<rect width="104" height="104" fill="url(#circle)"/>
<text x="110" y="53" dominant-baseline="middle" font-size="20"
font-family="sans-serif">Tu Coleccion</text>
</svg>

How to make an Scale from the center while hovering using framer motion

below i got an svg icon that scale up in size while hovering but it doesnt seem to scale from the center , how i can fix that? appreciate your feedback
<motion.svg
xmlns="http://www.w3.org/2000/svg"
height="42px"
viewBox="0 0 24 24"
width="42px"
fill="#FFFFFF"
whileHover={{
scale: 1.3,
}}
>
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
<path d="M9 21h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.58 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2zM9 9l4.34-4.34L12 10h9v2l-3 7H9V9zM1 9h5v12H1z" />
</motion.svg>
The width and height don't match the viewBox properties (24 vs 42). If you make those match it should scale from the center.
Edit:
You might have better luck wrapping the SVG in a div and using that to set the size you want:
<motion.div
whileHover={{
scale: 1.3
}}
style={{ width: 64, height: 64 }}
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="100%"
width="100%"
viewBox="0 0 24 24"
fill="#000"
>
<path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" />
<path d="M9 21h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.58 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2zM9 9l4.34-4.34L12 10h9v2l-3 7H9V9zM1 9h5v12H1z" />
</svg>
</motion.div>

Calculate dashed progress path in SVG circle

I'm building a progress circle that represents a users level up to three. It contains three dashes around the perimeter. A darker line that overlaps each dash will represent the user's progress. If a user is level one, the first dash should be overlapped by an amount equal to (their current experience / total experience to next level). If the user is level two, the first dash should be fully overlapped and the second dash should be the same as the above and so on. I've built my SVG circle with the following code:
<svg
width="240"
height="240"
viewBox="0 0 240 240"
strokeWidth="2px"
strokeDasharray="220 10"
strokeDashoffset="50"
>
<circle cx="120" cy="120" r="110" fill="white" stroke="turquoise" />
</svg>
This image represents a user that is on level two and about halfway through to level three.
How could I calculate a <path> to overlap the circle and display the user's progress?
This is how you'd do it with an arc path. Draw the arc from the bottom of the circle to +1 unit in the x axis. Set the pathLength="100" so you can calculate the required dash array appropriately. Then make sure your dash-array adds up to 100.
Note - you can't use a round line-cap for this method - there is a bug in Chrome that adds an incorrect line-cap at the end of the stroke-dash array.
<svg
width="240px"
height="240px"
viewBox="0 0 240 240"
stroke-width="4"
stroke-dasharray="220 10"
stroke-dashoffset="50"
stroke-linecap ="none">
<circle cx="120" cy="120" r="110" fill="white" stroke="turquoise" />
<path pathLength="100" d="M120 230 a 110 110 0 1 1 1 0" stroke="red" fill="none" stroke-width="8" stroke-dasharray="0 1 32 1 15 50" stroke-dashoffset="0"/>
</svg>
I question your design for readability:
This design is based on Google Analystics graphs:
Dev To Blogpost on how to built Graphs with Web Components
Web Component source: https://progress-circle.github.io/element.js:
All HTML Required:
<script src="https://progress-circle.github.io/element.js"></script>
<progress-circle edge="grey">
<style>
svg {
font: 16px arial;
height: 180px;
}
</style>
<progress value="75%" stroke="green">Level I</progress>
<progress value="60%" stroke="orange">Level II</progress>
<progress value="50%" stroke="orangered">Level III</progress>
</progress-circle>
Here you can control the length of each path by setting the stroke-dasharray. The first number is the length (between 0 and 94 (2*PI*45/3 = 94)) and the second number is the space between strokes (this should just be more than 284).
In this example the red path is 50% length. You need to experiment a bit with the numbers.
var dist = 6;
console.log('Length of 1/3 circle:', Math.round(2 * Math.PI * 45 / 3)-dist);
console.log('Rotate <g> slightly:',-Math.round(2 * Math.PI * 45 / 12)-(dist/2));
<div id="progress">
<svg viewBox="0 0 100 100" width="300" height="300">
<g transform="translate(50 50) rotate(-27)">
<path transform="rotate(0)" d="M 45,0 A 45,45 0 0 1 0,45 45,45 0 0 1 -45,0 45,45 0 0 1 0,-45 45,45 0 0 1 45,0 Z" stroke-width="5" stroke-dasharray="44 400" stroke-dashoffset="0" stroke="red" fill="none" stroke-linecap="round"/>
<path transform="rotate(120)" d="M 45,0 A 45,45 0 0 1 0,45 45,45 0 0 1 -45,0 45,45 0 0 1 0,-45 45,45 0 0 1 45,0 Z" stroke-width="5" stroke-dasharray="88 400" stroke-dashoffset="0" stroke="navy" fill="none" stroke-linecap="round"/>
<path transform="rotate(240)" d="M 45,0 A 45,45 0 0 1 0,45 45,45 0 0 1 -45,0 45,45 0 0 1 0,-45 45,45 0 0 1 45,0 Z" stroke-width="5" stroke-dasharray="88 400" stroke-dashoffset="0" stroke="orange" fill="none" stroke-linecap="round"/>
</g>
</svg>
</div>

Shape "8" looped animation with SVG <animate> only

I'm having massive headaches trying to fin the sweet-spot to achieve "8" shape animation using the combination of just <animate attributeName="cx"> and <attributeName="cy">.
I'll like to do it using that since it seems, by my metrics, that it's the most performant in terms of FPS, CPU and GPU usage.
Quick demo of the "ideal" motion path: https://codepen.io/ivancis/pen/eYmZowz
New solution caused by refinements in comments
I need to animate a svg element using just a combination of
<animate attributeName="cx"> <animate attributeName="cy"> (for
performance) to make an "8" shape motion, looped
Since the author does not want to use the animateMotion command, in this case
  I see only one way to implement the animation of the movement of the circle along the infinity symbol:
It is necessary to sequentially select many points along the infinity symbol and assign their coordinates to the circle cx = "x", cy = "y"
The more points you select, the closer the trajectory moving of the circle along the infinity symbol
In the vector editor, I sequentially put circles on the infinity symbol and wrote down their coordinates of the center of the circle. The first circle has the center coordinates cx ="70" cy ="60"
So, it was done for all circles located along the infinity symbol. The last circle has the same coordinates as the first one, thereby realizing a closed cycle
It remains only to substitute these values in the animation formulas cx, cy
Circle motion animation cx, cy with radius r="5
<div class="group">
<svg class="ball" xmlns="http://www.w3.org/2000/svg" width="50%" height="50%" viewBox="0 0 120 120">
<circle fill="olive" cx="70" cy="60" r="5">
<animate
attributeName="cx"
attributeType="XML"
repeatCount="indefinite"
begin="0s"
dur="2s"
values="70;65;60;55;50;45;40.5;40.5;42.5;45.1;48.7;52;55;58;60;61;61;61;61;61;61;62.9;66;69;
73;76;79;81;80;78;74;70">
</animate>
<animate
attributeName="cy"
attributeType="XML"
repeatCount="indefinite"
begin="0"
dur="2s"
values="60;60;60;60;60;58.3;52.5;47.9;44.4;41.8;40.3;40;41;43;47;51;55;60;65;70;74;77;79;
80;80;79;76;72;67;64;61;60">
</animate>
</circle>
<path fill="none" stroke="black" stroke-dasharray="2" d="M70.5,60.5c5.5,0,10,4.5,10,10s-4.5,10-10,10s-10-4.5-10-10v-20c0-5.5-4.5-10-10-10s-10,4.5-10,10 s4.5,10,10,10H70.5z"/>
</svg>
</div>
Radius r = 40 as in the example of the author of the question
<div class="group">
<svg class="ball" xmlns="http://www.w3.org/2000/svg" width="50%" height="50%" viewBox="0 0 120 120">
<circle fill="olive" cx="70" cy="60" r="40">
<animate
attributeName="cx"
attributeType="XML"
repeatCount="indefinite"
begin="0s"
dur="2s"
values="70;65;60;55;50;45;40.5;40.5;42.5;45.1;48.7;52;55;58;60;61;61;61;61;61;61;62.9;66;69;
73;76;79;81;80;78;74;70">
</animate>
<animate
attributeName="cy"
attributeType="XML"
repeatCount="indefinite"
begin="0"
dur="2s"
values="60;60;60;60;60;58.3;52.5;47.9;44.4;41.8;40.3;40;41;43;47;51;55;60;65;70;74;77;79;
80;80;79;76;72;67;64;61;60">
</animate>
</circle>
<path fill="none" stroke="black" stroke-dasharray="2" d="M70.5,60.5c5.5,0,10,4.5,10,10s-4.5,10-10,10s-10-4.5-10-10v-20c0-5.5-4.5-10-10-10s-10,4.5-10,10 s4.5,10,10,10H70.5z"/>
</svg>
</div>
You didn’t say what kind of animation you really want.
Therefore, I will offer examples of different types of animation and you can choose any them and somehow modify for yourself.
Infinity Symbol Moving Animation
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="100" viewBox="0 0 100 100">
<path fill="none" stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10">
<animateTransform
attributeName="transform"
type="translate"
values="0; 150; 0"
begin="0s"
dur="4s"
repeatCount="indefinite" />
</path>
</svg>
Rotation
Mouse over symbol
.infinity1{
transform-box: fill-box;
transform-origin: center center;
transition: rotate 2s linear ;
}
.infinity1:hover {
animation: spin 2s linear infinite;
}
#keyframes spin {
100% {transform :rotate(360deg);}
}
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
<path class="infinity1" fill="none" stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10" />
</svg>
Rotation around the axis Y
.infinity1{
transform-box: fill-box;
transform-origin: center center;
transition: rotate 2s linear ;
fill:transparent;
}
.infinity1:hover {
animation: spin 2s linear infinite;
}
#keyframes spin {
100% {transform :rotateY(360deg);}
}
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" >
<path class="infinity1" stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10" />
</svg>
Animation of infinity symbol filling by changing attribute stroke-dasharray
Click on the colored letters in circles
.container {
width:40%;
height="40%";
background:black;
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 20 100 100">
<path fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10" />
<!-- The midpoint of the beginning of the animation in the center of the figure. stroke-dashoffset="31.1" -->
<path id="center" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="crimson" stroke-width="10" stroke-dashoffset="31.1" stroke-dasharray="0 128.5" >
<animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_C.click" dur="4s" restart="whenNotActive" />
</path>
<!-- Middle point on the left stroke-dashoffset="-159.5" -->
<path id="Left" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="yellowgreen" stroke-width="10" stroke-dashoffset="-159.5" stroke-dasharray="0 128.5" >
<animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_L.click" dur="4s" restart="whenNotActive" />
</path>
<!-- Midpoint left top stroke-dashoffset="128.5" -->
<path id="Top" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="gold" stroke-width="10" stroke-dashoffset="128.5" stroke-dasharray="0 128.5" >
<animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_T.click" dur="4s" restart="whenNotActive" />
</path>
<!-- Midpoint lower right stroke-dashoffset="192.7" -->
<path id="Bottom" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="dodgerblue" stroke-width="10" stroke-dashoffset="192.7" stroke-dasharray="0 128.5" >
<animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_B.click" dur="4s" restart="whenNotActive" />
</path>
<!-- Middle point on the right stroke-dashoffset="223.9" -->
<path id="Bottom" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="purple" stroke-width="10" stroke-dashoffset="223.9" stroke-dasharray="0 128.5" >
<animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_R.click" dur="4s" restart="whenNotActive" />
</path>
<g id="btn_L" transform="translate(-17 0)" >
<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
<text x="25" y="95" font-size="10" fill="green" >L</text>
</g>
<g id="btn_C" transform="translate(3 0)">
<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
<text x="24" y="95" font-size="10" fill="crimson" >C</text>
</g>
<g id="btn_T" transform="translate(23 0)">
<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
<text x="24" y="95" font-size="10" fill="orange" >T</text>
</g>
<g id="btn_B" transform="translate(43 0)">
<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
<text x="25" y="95" font-size="10" fill="dodgerblue" >B</text>
</g>
<g id="btn_R" transform="translate(63 0)">
<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
<text x="25" y="95" font-size="10" fill="purple" >R</text>
</g>
</svg>
</div>
Live Demo
I was looking into a similar thing and came across this answer. #Alexandr_TT's answer got me thinking about a more flexible way to do this, without using a graphics editor (like Inkscape etc.)
I came up with the following idea:
Use the <AnimateMotion/> for the first loop.
Fire a setInterval every X milliseconds and each time it fires to capture the centre point of the circle (from circle.getBoundingClientRect() and svg.matrixTransform())
Push these x and y values to two arrays to capture them
When the AnimateMotion ends, clear the current setInterval and push the first element also to the end of each of the arrays (to close the loop)
Remove the <AnimateMotion/> tag from the DOM
Push these arrays to the values attribute of the <animate id="cx" attributeName="cx" values="" .../> and <animate id="cy" attributeName="cy" values="" .../> tags
begin both of these animate tags with cx.beginElement() and cy.beginElement()
You could just be happy with this performance-wise, or you could copy-paste the DOM elements with their values="..." attributes and save that as your new master file, essentially achieving what #Alexandr_TT did with the graphics editor. Of course, this method I am showing is flexible if you decide to change your path etc.
Demo: https://codepen.io/Alexander9111/pen/VwLaNEN
HTML:
<circle id="circle" class="circle" cx="0" cy="00" r="125">
<animateMotion
path="M162.9,150c6.8-0.2,12.1-5.7,12.1-12.5c0-6.9-5.6-12.5-12.5-12.5c-6.8,0-12.3,5.4-12.5,12.2v25.7 c-0.2,6.8-5.7,12.2-12.5,12.2c-6.9,0-12.5-5.6-12.5-12.5c0-6.8,5.4-12.3,12.1-12.5L162.9,150z"
dur="4s" begin="0s"
epeatCount="1" fill="freeze"
calcMode="linear"
fill="freeze">
</animateMotion>
<animate id="cx" attributeName="cx" values="" dur="4s" repeatCount="indefinite" begin="indefinite"/>
<animate id="cy" attributeName="cy" values="" dur="4s" repeatCount="indefinite" begin="indefinite"/>
</circle>
JS:
const svg = document.querySelector('svg');
const animateElem = document.querySelector('animateMotion');
const circle = document.querySelector('#circle');
const cx = document.querySelector('#cx');
const cy = document.querySelector('#cy');
let myInterval;
let valuesX = [];
let valuesY = [];
function startFunction() {
const box = circle.getBoundingClientRect();
var pt = svg.createSVGPoint();
pt.x = (box.left + box.right) / 2;
pt.y = (box.top + box.bottom) / 2;
var svgP = pt.matrixTransform(svg.getScreenCTM().inverse());
console.log(svgP.x,svgP.y)
valuesX.push(svgP.x);
valuesY.push(svgP.y);
}
function endFunction() {
animateElem.parentNode.removeChild(animateElem);
clearInterval(myInterval)
valuesX.push(valuesX[0]);
valuesY.push(valuesY[0]);
cx.setAttribute('values', valuesX.join('; '));
cy.setAttribute('values', valuesY.join('; '));
circle.setAttribute('cx', 0);
circle.setAttribute('cy', 0);
cx.beginElement();
cy.beginElement();
}
animateElem.addEventListener('beginEvent', () => {
console.log('beginEvent fired');
myInterval = setInterval(startFunction, 50);
})
animateElem.addEventListener('endEvent', () => {
console.log('endEvent fired');
endFunction();
})

SVG how to make container bigger than svg

Have this problem
The blue circle (#Mark) should have black round border. For some reason it is not. Although if it is not a svg sprite but two separate svg elements everything is ok.
SVG:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="r8qna" d="M1351 35.438c0 .983-.815 1.562-1.821 1.562h-4.857c0 .984-.816 1.781-1.822 1.781-1.006 0-1.821-.797-1.821-1.781h-4.857c-1.007 0-1.822-.579-1.822-1.562 0-.846.376-1.649 1.03-2.202 1.17-.99 2.006-3.618 2.006-6.705 0-2.337 1.536-4.318 3.673-5.044A1.806 1.806 0 0 1 1342.5 20c.903 0 1.647.644 1.791 1.487 2.137.726 3.673 2.707 3.673 5.044 0 3.088.837 5.716 2.007 6.705a2.882 2.882 0 0 1 1.03 2.202zm-1.568-.103c0-.34-.15-.663-.414-.886-1.688-1.428-2.737-4.296-2.737-8.024 0-2.039-1.696-3.697-3.78-3.697-2.086 0-3.782 1.658-3.782 3.697 0 3.728-1.049 6.596-2.737 8.023a1.162 1.162 0 0 0-.414.887z"/>
<path id="rfoga" d="M1074.5 101a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5z"/>
</defs>
<symbol id="Bell" viewBox="0 0 17 19">
<g>
<g transform="translate(-1334 -20)">
<use fill="#a56ea3" xlink:href="#r8qna"/>
<use fill="#e0e0e0" xlink:href="#r8qna"/>
</g>
</g>
</symbol>
<symbol id="Mark" viewBox="0 0 5 5">
<g>
<g transform="translate(-1072 -101)">
<use fill="#30a1d6" xlink:href="#rfoga"/>
</g>
</g>
</symbol>
</svg>
CSS code
&__bell-icon {
fill: #e0e0e0;
height: 19px;
width: 17px;
}
&__circle-icon {
position: absolute;
visibility: visible;
height: 5px;
width: 5px;
stroke:$icon-stroke-light;
}
HTML code:
<span class="notifications__icons">
<svg class="notifications__bell-icon"><use href="../../../images/BellWithMark.svg#Bell"></svg>
<svg class="notifications__circle-icon"><use href="../../../images/BellWithMark.svg#Mark"></svg>
</span>
Your #Mark symbol is too small. You need to add some space for the stroke. Use <symbol id="Mark" viewBox="-1 -1 7 7"> instead of <symbol id="Mark" viewBox="0 0 5 5">. If needed change CSS accordingly.
I needed to see what happens so I've changed the size of your icons.
.notifications__bell-icon {
fill: #e0e0e0;
height: 190px;
width: 170px;
position: absolute;
}
.notifications__circle-icon {
position: absolute;
visibility: visible;
height: 50px;
width: 50px;
stroke:black;
}
<span class="notifications__icons">
<svg class="notifications__bell-icon"><use href="#Bell"></svg>
<svg class="notifications__circle-icon"><use href="#Mark"></svg>
</span>
<svg>
<defs>
<path id="r8qna" d="M1351 35.438c0 .983-.815 1.562-1.821 1.562h-4.857c0 .984-.816 1.781-1.822 1.781-1.006 0-1.821-.797-1.821-1.781h-4.857c-1.007 0-1.822-.579-1.822-1.562 0-.846.376-1.649 1.03-2.202 1.17-.99 2.006-3.618 2.006-6.705 0-2.337 1.536-4.318 3.673-5.044A1.806 1.806 0 0 1 1342.5 20c.903 0 1.647.644 1.791 1.487 2.137.726 3.673 2.707 3.673 5.044 0 3.088.837 5.716 2.007 6.705a2.882 2.882 0 0 1 1.03 2.202zm-1.568-.103c0-.34-.15-.663-.414-.886-1.688-1.428-2.737-4.296-2.737-8.024 0-2.039-1.696-3.697-3.78-3.697-2.086 0-3.782 1.658-3.782 3.697 0 3.728-1.049 6.596-2.737 8.023a1.162 1.162 0 0 0-.414.887z"/>
<path id="rfoga" d="M1074.5 101a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5z"/>
</defs>
<symbol id="Bell" viewBox="0 0 17 19">
<g>
<g transform="translate(-1334 -20)">
<use fill="#a56ea3" xlink:href="#r8qna"/>
<use fill="#e0e0e0" xlink:href="#r8qna"/>
</g>
</g>
</symbol>
<symbol id="Mark" viewBox="-1 -1 7 7">
<g>
<g transform="translate(-1072 -101)">
<use fill="#30a1d6" xlink:href="#rfoga"/>
</g>
</g>
</symbol>
</svg>

Resources