MartiniBaby
MartiniBaby

Reputation: 33

Animate-duration is not accurate

I created loading spinner in SVG, but animation-duration is not accurate. For example:

I'm desperate and I don't know where there may be a mistake. Can you help me?

Screen of spinner #1

enter image description here

Screen of spinner #2 - later

enter image description here

<svg class="circle">
  <circle cx="23" cy="23" r="20"/>
</svg>

Less:

@spinnerSize: 46;

svg.spinner {
  display: block;
  width: (@spinnerSize + 0px);
  height: (@spinnerSize + 0px);
  x: 0px;
  y: 0px;
  background: url("../images/ico_timer_small.png") center center no-repeat;

  circle {
    fill: transparent;
    stroke: #027eff;
    stroke-width: 3;
    stroke-dasharray: (3.14 * @spinnerSize);
    transform-origin: (0.5px * @spinnerSize) (0.5px * @spinnerSize) 0;
    transform: rotate(-90deg);
    animation-name: spinner;
    animation-timing-function: linear;
    animation-duration: 30s;
    stroke-linecap: butt;
  }
}

@keyframes spinner {
  from {
    stroke-dashoffset: (3.14 * @spinnerSize);
  }
  to {
    stroke-dashoffset: 0;
  }
}

Upvotes: 3

Views: 450

Answers (1)

Harry
Harry

Reputation: 89770

The stroke-dasharray value should be equal to the circumference of the circle for this animation to work properly. The circle's radius is only 20 and so the circumference (2 * PI * radius) is equal to 125.66 but in the Less code you have set the diameter (@spinnerSize) as 46 and because of this, the stroke-dasharray has a computed value of 144.44 (greater than circumference of circle).

For a value to go from 0 to 144.44 in 30s, it must be incremented (approximately) by 4.81 per second and so by the time it reaches the 26s mark, the value becomes (26 * 4.81) = 125.81 (approximately). Since this value is greater than the circumference, it looks like the animation has completed ahead of time whereas in reality it is still animating the value until it reaches 144.44.

In the below snippet, I've set 125 as the final value and it runs as expected for 30 seconds. In Less code, you need to calculate stroke-dasharray based on two times the radius of the circle. Do not directly modify the value of @spinnerSize variable because that would modify a few other properties and end up affecting the display of the SVG circle.

svg.spinner {
  display: block;
  width: 46px;
  height: 46px;
  /*x: 0px;
  y: 0px;*/
  background: url("../images/ico_timer_small.png") center center no-repeat;
}
svg.spinner circle {
  fill: transparent;
  stroke: #027eff;
  stroke-width: 3;
  stroke-dasharray: 125;
  transform-origin: 23px 23px 0;
  transform: rotate(-90deg);
  animation-name: spinner;
  animation-timing-function: linear;
  animation-duration: 30s;
  stroke-linecap: butt;
}
@keyframes spinner {
  from {
    stroke-dashoffset: 125;
  }
  to {
    stroke-dashoffset: 0;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<svg class="spinner">
  <circle cx="23" cy="23" r="20" />
</svg>

1. Prefix-free library is used in snippet only to avoid browser prefixes.
2. I've used the compiled CSS generated by the Online Less compiler at Less2CSS.

Upvotes: 6

Related Questions