SVG circle with multiple origin for stroke animation - css

I have a simple donut graph with fill-in animation. The problem is that I get two separate paths. (Es. 10% bar gives me 0-10% and then space and then another 10%.
I have tried playing around with the different variables but I can't figure out what I am doing wrong, any help? I have used this: https://codepen.io/matttherat/pen/EeMaEw?editors=1100
Here's a screen:
.svg-item {
flex: 1;
font-size: 16px;
max-width: 120px;
animation: donutfade 1s;
margin: 0 auto;
}
.data-des {
font-size: 0.7em;
display: block;
font-weight: bold;
text-align: center;
margin-top: 10px;
}
#keyframes donutfade {
/* this applies to the whole svg item wrapper */
0% {
opacity: 0.2;
}
100% {
opacity: 1;
}
}
.donut-ring-ext {
stroke: #50b180;
}
.donut-segment {
transform-origin: center;
}
.donut-segment-2 {
stroke: #a8df8a;
animation: donut1 1s;
}
.donut-segment-3 {
stroke: #a8df8a;
animation: donut2 1s;
}
.donut-segment-4 {
stroke: #a8df8a;
animation: donut3 1s;
}
.donut-percent {
color: #3c8560;
animation: donutfadelong 1s;
}
#keyframes donutfadelong {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes donut1 {
0% {
stroke-dasharray: 0 100;
}
100% {
stroke-dasharray: 10 90;
}
}
#keyframes donut2 {
0% {
stroke-dasharray: 0, 100;
}
100% {
stroke-dasharray: 20, 80;
}
}
#keyframes donut3 {
0% {
stroke-dasharray: 0, 100;
}
100% {
stroke-dasharray: 50, 50;
}
}
.donut-label {
font-size: 0.28em;
font-weight: 700;
line-height: 1;
fill: #000;
transform: translateY(0.25em);
}
.donut-percent {
font-size: 0.5em;
line-height: 1;
transform: translateY(0.5em);
font-weight: 100;
}
.donut-data {
font-size: 0.12em;
line-height: 1;
transform: translateY(0.5em);
text-align: center;
text-anchor: middle;
color: #666;
fill: #666;
animation: donutfadelong 1s;
}
<div class="svg-item">
<svg width="100%" height="100%" viewBox="0 0 40 40" class="donut">
<circle
class="donut-hole"
cx="20"
cy="20"
r="15.91549430918954"
fill="#fff"
></circle>
<circle
class="donut-ring-ext"
cx="20"
cy="20"
r="19"
fill="transparent"
stroke-width="2"
></circle>
<circle
class="donut-segment donut-segment-2"
cx="20"
cy="20"
r="22"
fill="transparent"
stroke-width="2"
stroke-dasharray="10 90"
stroke-dashoffset="-5"
></circle>
<g class="donut-text donut-text-1">
<text y="50%" transform="translate(0, 2)">
<tspan
x="50%"
text-anchor="middle"
class="donut-percent"
>
10%
</tspan>
</text>
</g>
<span class="data-des">Amet dolorem sit</span>
</svg>
</div>

You need to animate the stroke-dashoffset attribute. At the beginning stroke-dasharay = the path's total length (calculated with .getTotalLength()). Since you are using only one value the dashes and the gaps are of equal length.
stroke-dasharray="137.35"
Also the stroke-dashoffset="137.35". This means that you don't see the dash. In this moment your stroke is the gap.
Next you are animating the stroke-dashoffset. If you want to see 10% of the dash yoi need to animate the stroke-dashoffset from 100% to 90% i.e
100% {
stroke-dashoffset: 123.6;
}
I hope it helps.
.svg-item {
flex: 1;
font-size: 16px;
max-width: 400px;
margin: 0 auto;
}
.data-des {
font-size: 0.7em;
display: block;
font-weight: bold;
text-align: center;
margin-top: 10px;
}
.donut-ring-ext {
stroke: #50b180;
}
.donut-segment {
transform-origin: center;
}
.donut-segment-2 {
stroke: #a8df8a;
animation: donut1 1s forwards;
}
.donut-segment-3 {
stroke: #a8df8a;
animation: donut2 1s;
}
.donut-segment-4 {
stroke: #a8df8a;
animation: donut3 1s;
}
.donut-percent {
color: #3c8560;
animation: donutfadelong 1s;
}
#keyframes donut1 {
100% {
stroke-dashoffset: 123.6;
}
}
.donut-label {
font-size: 0.28em;
font-weight: 700;
line-height: 1;
fill: #000;
transform: translateY(0.25em);
}
.donut-percent {
font-size: 0.5em;
line-height: 1;
transform: translateY(0.5em);
font-weight: 100;
}
.donut-data {
font-size: 0.12em;
line-height: 1;
transform: translateY(0.5em);
text-align: center;
text-anchor: middle;
color: #666;
fill: #666;
animation: donutfadelong 1s;
}
svg{border:1px solid}
<div class="svg-item">
<svg viewBox="-30 -10 100 100" class="donut">
<g transform="rotate(-90 20 20)">
<circle
class="donut-hole"
cx="20"
cy="20"
r="15.91549430918954"
fill="#f00"
></circle>
<circle
class="donut-ring-ext"
cx="20"
cy="20"
r="19"
fill="transparent"
stroke-width="2"
></circle>
<circle
class="donut-segment donut-segment-2"
cx="20"
cy="20"
r="22"
fill="transparent"
stroke-width="2"
stroke-dasharray="137.35"
stroke-dashoffset="137.35"
></circle></g>
<g class="donut-text donut-text-1">
<text y="50%" transform="translate(0, 2)">
<tspan
x="50%"
text-anchor="middle"
class="donut-percent"
>
10%
</tspan>
</text>
</g>
<span class="data-des">Amet dolorem sit</span>
</svg>
</div>

Related

CSS animation rendering - staggering

I have created this CodePen for a rotating border around a button and although the principle seems to be good, it is not working as it should - the rendering seems extremely slow and staggering (I have the M1 MacBook Pro).
.button {
width: 206px;
height: 70px;
line-height: 70px;
text-align: center;
position: relative;
}
.button::after {
content: "";
border-radius: 35px;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
clip-path: url(#mask);
border: 3px solid black;
}
.curve-1 {
animation: ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ 20s infinite linear;
transform-origin: 35px center;
//animation-play-state: paused;
//transform: rotate(-315deg)
}
.curve-2 {
animation: ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ 20s infinite linear;
transform-origin: 35px center;
translate: 136px 0;
}
.line {
animation: ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ•̫͡•ʕ•̫͡•ʔ 20s infinite linear;
}
svg {
display: block;
}
#keyframes ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ {
0% {
transform: rotate(45deg);
}
22% {
transform: rotate(-135deg);
}
27% {
transform: rotate(-180deg);
}
88% {
transform: rotate(-180deg);
}
89% {
transform: rotate(-225deg);
}
100% {
transform: rotate(-315deg);
}
}
#keyframes ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ {
0% {
transform: rotate(0deg);
}
38% {
transform: rotate(0deg)
}
39% {
transform: rotate(-45deg);
}
50% {
transform: rotate(-135deg);
}
72% {
transform: rotate(-360deg);
}
100% {
transform: rotate(-360deg);
}
}
#keyframes ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ•̫͡•ʔ•̫͡•ʕ•̫͡•ʔ {
0%, 100% {
width: 0;
x: 35px;
y: 35px;
}
11% {
width: 0;
x: 35px;
y: 35px;
}
22% {
width: 55px;
x: 35px;
y: 35px;
}
39% {
width: 55px;
x: 116px;
y: 35px;
}
50% {
width: 0;
x: 171px;
y: 35px;
}
61% {
width: 0;
x: 171px;
y: 0;
}
72% {
width: 55px;
x: 116px;
y: 0;
}
89% {
width: 55px;
x: 35px;
y: 0;
}
100% {
width: 0;
x: 35px;
y: 0;
}
}
<div class="button">button text</div>
<svg view-box="0 0 206 70" width="206" height="70">
<defs>
<clipPath id="mask">
<path d="M10.2513 10.1652C3.73208 16.8036 0.103643 25.7716 0.165885 35.0968C0.228128 44.422 3.97596 53.3408 10.5832 59.8905L35.1651 34.8632L10.2513 10.1652Z" fill="#000" class="curve-1"/>
<path d="M10.2513 10.1652C3.73208 16.8036 0.103643 25.7716 0.165885 35.0968C0.228128 44.422 3.97596 53.3408 10.5832 59.8905L35.1651 34.8632L10.2513 10.1652Z" fill="#000" class="curve-2"/>
<rect fill="#000" height="35" class="line">
</clipPath>
</defs>
</svg>
<svg view-box="0 0 206 70" width="206" height="70">
<path d="M10.2513 10.1652C3.73208 16.8036 0.103643 25.7716 0.165885 35.0968C0.228128 44.422 3.97596 53.3408 10.5832 59.8905L35.1651 34.8632L10.2513 10.1652Z" fill="#000" class="curve-1"/>
<path d="M10.2513 10.1652C3.73208 16.8036 0.103643 25.7716 0.165885 35.0968C0.228128 44.422 3.97596 53.3408 10.5832 59.8905L35.1651 34.8632L10.2513 10.1652Z" fill="#000" class="curve-2"/>
<rect fill="#000" height="35" class="line">
</svg>
Does anybody know anything about CSS rendering and why could this be happening?
I also created this CodePen where I just wanted to demonstrate that animating a clip-path is possible and it seems to work just fine here...
.masked {
width: 500px;
clip-path: url(#mask)
}
.mask {
width: 500px;
}
.circle {
animation: ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ 2s infinite;
}
#keyframes ʕ•̫͡•ʕ•̫͡•ʔ•̫͡•ʔ {
0% {
translate: 0;
}
50% {
translate: 40px;
}
100% {
translate: 100px;
}
}
<img src="https://images.unsplash.com/photo-1542273917363-3b1817f69a2d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Nnx8dHJlZXxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=900&q=60" class="masked"/>
<svg viewport="0 0 100 60">
<circle cx="50" cy="30" r="20" class="circle">
</svg>
<svg width="0" height="0">
<defs>
<clipPath id="mask">
<circle cx="100" cy="100" r="40" class="circle" />
</clipPath>
</defs>
</svg>
I have no idea why this's happening, if anyone knows the answer, I would be glad.
Thanks
Why so complicated? The same animation can be achieved with the stroke-dasharray technique.
For me, the animation appears smooth. Whether it is the same for you depends probably on hardware, but I am far down from your computing power.
.button {
width: 206px;
height: 70px;
line-height: 70px;
text-align: center;
position: relative;
border: 3px solid transparent;
}
.button .line {
position: absolute;
overflow: visible;
top: 0;
left: 0;
width: 100%;
height: 100%;
fill: none;
stroke: black;
stroke-width: 3px;
stroke-dasharray: 10px 90px;
animation: around 20s linear infinite;
}
#keyframes around {
0% {
stroke-dashoffset: 100px;
}
100% {
stroke-dashoffset: 0px;
}
}
<div class="button">button text<svg class="line" view-box="0 0 206 70">
<path d="M35,-1.5 A 36.5 36.5 0 0 0 35,71.5 H 171 A 36.5 36.5 0 0 0 171,-1.5 Z" pathLength="100" />
</svg>

Invert the left svg animation?

Here is my code https://codepen.io/victoreugen2002/pen/PoJJPzP
I am trying to animate the left and right svg, to acheive this:
both starting at the same time from the top and they continue drawing.
until they both met at the bottom in the middle.
In order to acheive this I am thinking of inversing the animation for left svg, but it's not working:
.left {
width: 50%;
stroke-dasharray: 1855.968505859375;
animation: dash 2s ease-in;
#keyframes dash {
from {
stroke-dashoffset: 1855.968505859375;
}
to {
stroke-dashoffset: 3710;
}
}
svg {
left: 0;
}
}
You had to go from -1855.968505859375 to 0.
.left {
width: 50%;
stroke-dasharray: 1855.968505859375;
animation: dash-left 2s ease-in;
}
.left svg {
left: 0;
}
.right {
width: 50%;
stroke-dasharray: 1855.968505859375;
animation: dash-right 2s ease-in;
}
.right svg {
right: 0;
}
svg {
position: absolute;
top: 20px;
width: 50%;
stroke-width: 5px;
}
#keyframes dash-left {
from { stroke-dashoffset: -1855.968505859375 }
to { stroke-dashoffset: 0 }
}
#keyframes dash-right {
from { stroke-dashoffset: 1855.968505859375 }
to { stroke-dashoffset: 0 }
}
<div class="left">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733 425">
<path d="M733,424.5H39A38.5,38.5,0,0,1,.5,386V39A38.5,38.5,0,0,1,39,.5H733" style="fill: none;stroke: #78be21"/>
</svg>
</div>
<div class="right">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733 425">
<path d="M0,.5H694A38.5,38.5,0,0,1,732.5,39V386A38.5,38.5,0,0,1,694,424.5H0" style="fill: none;stroke: #78be21"/>
</svg>
</div>

SVG Animation - CSS Hover Animation For SVG From The Center

I have done this effect in GSAP here is the Codepen as a reference:
https://codepen.io/whitelionx/full/vYGQqBZ
const svgs = document.querySelectorAll("svg");
svgs.forEach((svg) => {
const tl = gsap
.timeline({
defaults: { ease: "power1.in" },
paused: true
})
.to(svg.children[0], { drawSVG: "50% 50%" })
.from(svg.children[1], { drawSVG: "0% 0%" }, 0);
svg.addEventListener("mouseenter", () => tl.play());
svg.addEventListener("mouseleave", () => tl.reverse());
});
Now I wanna do it with CSS solely so that when I hover my svg I get the same effect, here is my code sandbox:
https://codesandbox.io/s/xenodochial-benz-17lss?file=/src/styles.css
I've modified things to animate the stroke-dasharray instead.
body {
background: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
svg {
width: 50px;
height: 50px;
margin: 25px;
}
.circle {
stroke-dasharray: 28.3,0,28.3;
transform-origin: 50% 50%;
transform: rotate(180deg);
transition: stroke-dasharray 0.5s linear;
}
.line {
stroke-dasharray: 20;
stroke-dashoffset: 20;
transition: stroke-dashoffset 0.5s linear;
}
svg:hover .circle {
stroke-dasharray: 0,56.0;
}
svg:hover .line {
stroke-dashoffset: 0;
}
<svg
version="1.1"
shape-rendering="auto"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 20 20"
xml:space="preserve">
<path class="circle" fill="none" stroke="#FFFFFF" stroke-miterlimit="10" d="M10,1c5,0,9,4,9,9s-4,9-9,9s-9-4-9-9S5,1,10,1z"/>
<path class="line" fill="none" stroke="#FFFFFF" stroke-miterlimit="10" d="M10,0v20"/>
</svg>
I also did it in the meantime and now I get better understanding of css animations thanks to your answer too :D thinking out of the box
body {
background: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
svg {
width: 50px;
height: 50px;
margin: 25px;
cursor: pointer;
}
.circle {
stroke-dasharray: 56.6;
stroke-dashoffset: 0;
transform-origin: 50% 50%;
transition: stroke-dashoffset 0.3s linear, transform 0.3s linear;
}
.line {
stroke-dasharray: 20;
stroke-dashoffset: 20;
transition: stroke-dashoffset 0.3s linear;
}
svg:hover .circle {
stroke-dashoffset: 56.6;
transform: rotate(180deg);
}
svg:hover .line {
stroke-dashoffset: 0;
}
<svg
version="1.1"
shape-rendering="auto"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 20 20"
enable-background="new 0 0 20 20"
xml:space="preserve"
>
<path
class="circle"
fill="none"
stroke="#FFFFFF"
stroke-miterlimit="10"
d="M10,1c5,0,9,4,9,9s-4,9-9,9s-9-4-9-9S5,1,10,1z"
></path>
<path
class="line"
fill="none"
stroke="#FFFFFF"
stroke-miterlimit="10"
d="M10,0v20"
></path>
</svg>

Animate a rotating tooltip without rotating text itself (CSS)

I would like to create a tooltip which consists of a line that rotates around an icon when hovered, as the diagram below shows.
Rotation part is not difficult, but I am having trouble getting the text to stay parallel to the page. Any help, if possible with an example, would be much appreciated.
The closest I have come up with is:
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
align-items: center;
background-color: black;
color: white;
}
svg {
width: 256px;
height: 256px;
}
svg > g {
transform: translate(128px, 128px);
}
#ticks > line {
stroke: white;
}
#hands {
stroke: white;
stroke-width: 4px;
transform: rotate(-90deg);
}
#second, .label {
animation: handrotation 40s infinite alternate;
}
.label {
transform-origin: -100px;
position: relative;
left: 50px;
}
#second > line {
stroke: white;
stroke-width: 1px;
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 8s forwards;
}
#keyframes dash {
to {
stroke-dashoffset: 00;
}
}
#keyframes handrotation {
to {
transform: rotate(0.1turn);
}
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<g>
<g id="second">
<line x1="-12" y1="0" x2="120" y2="0"/>
</g>
<span class="label">label</span>
</g>
</g>
</svg>

How to do that animation begins after page is fully loaded?

I have animation (#svg_tag, .st0). After animation complete, then fades-in the image (.logo_svg). I want that animation begins after page is fully loaded (both animation and image).
.logo_svg {
max-width: 80%;
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
opacity: 0;
animation-name: show;
animation-delay: 11s;
animation-duration: 1s;
animation-fill-mode: forwards;
animation-iteration-count: 1;
animation-timing-function: linear;
}
body{
background-color: grey;
}
#svg_tag {
max-width: 80%;
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
.st0 {
fill-opacity: 0;
stroke: #fff;
stroke-width: 1;
stroke-dasharray: 1350;
stroke-dashoffset: 1350;
animation: draw 15s linear;
animation-delay: 5s;
}
#keyframes draw {
to {
stroke-dashoffset: 0;
}
}
#keyframes show {
to {
opacity: 1;
}
}
<body>
<svg version="1.1" id="svg_tag" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 708.7 437.3" style="enable-background:new 0 0 708.7 437.3;" xml:space="preserve">
<path id="XMLID_13_" class="st0" d="M708.7,285c-18.6,18.6-47.7,21.9-70,7.9v102.3l70,42V285z"/>
<path id="XMLID_12_" class="st0" d="M595.6,113.1l-113.1,67.9v7.5H509V245c0,16.6,13.4,30,30,30s30-13.4,30-30v-56.5h26.5v113.1
h-26.5v-8.6c-9,5.6-19.4,8.6-30,8.6c-31.2,0-56.5-25.3-56.5-56.5v56.5l129.6,77.8V188.5h26.5v8.6c22.3-14,51.4-10.7,70,7.9v-24.1
L595.6,113.1z"/>
<circle id="XMLID_11_" class="st0" cx="669" cy="245" r="30"/>
<path id="XMLID_10_" class="st0" d="M242.7,188.5h-9.9V245c0,25.7-20.9,46.6-46.6,46.6s-46.6-20.9-46.6-46.6v-56.5h-9.9V245
c0,31.2,25.3,56.5,56.5,56.5c18.6,0,36.1-9.2,46.6-24.5v24.5h9.9V188.5z"/>
<polyline id="XMLID_9_" class="st0" points="279.2,188.5 259.3,188.5 259.3,198.4 269.2,198.4 269.2,301.6 279.2,301.6 279.2,188.5
"/>
<path id="XMLID_8_" class="st0" d="M259.3,123c0-5.5,4.4-9.9,9.9-9.9s9.9,4.4,9.9,9.9s-4.4,9.9-9.9,9.9S259.3,128.5,259.3,123
L259.3,123z"/>
<rect id="XMLID_7_" x="295.7" y="113.1" class="st0" width="9.9" height="188.5"/>
<path id="XMLID_16_" class="st0" d="M425.4,0v213c-17.7-25.7-52.9-32.3-78.6-14.6c-25.7,17.7-32.3,52.9-14.6,78.6
c17.7,25.7,52.9,32.3,78.6,14.6c5.7-3.9,10.7-8.9,14.6-14.6v24.5h9.9V0H425.4z M378.8,291.6c-25.7,0-46.6-20.9-46.6-46.6
s20.9-46.6,46.6-46.6s46.6,20.9,46.6,46.6S404.5,291.6,378.8,291.6z"/>
<path id="XMLID_15_" class="st0" d="M103.1,213c-17.7-25.7-52.9-32.3-78.6-14.6c-5.7,3.9-10.7,8.9-14.6,14.6V0H0v301.6h9.9V277
c17.7,25.7,52.9,32.3,78.6,14.6C114.3,273.9,120.8,238.7,103.1,213z M56.5,291.6c-25.7,0-46.6-20.9-46.6-46.6s20.9-46.6,46.6-46.6
s46.6,20.9,46.6,46.6S82.3,291.6,56.5,291.6z"/>
</svg>
<img src="logo.png" class="logo_svg" alt="" />
</body>
Thanks for uploading the HTML.
Correct me if I'm wrong, but I think you want to animate the logo, to draw itself, when the page loads. And then you want to animate another logo, that fades in.
If the logotypes are the same - then you can animate the first SVG logo itself, with one animation. No need for two :)
Here's a little codepen that does exactly that.
Here's the essential animation:
svg{
max-width: 80%;
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
.st0 {
fill-opacity: 0;
stroke: #fff;
stroke-width: 1;
stroke-dasharray: 1350;
stroke-dashoffset: 1350;
animation: draw 5s linear forwards;
}
#keyframes draw {
95% {
stroke-dashoffset: 0;
fill-opacity:0;
stroke-width:1;
}
100%{
fill-opacity:1;
stroke-width:0;
}
}
Does this solve your problem?

Resources