Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG stroke-dasharray offset not consistent

I have a series of circles with borders comprised of smaller circles which I will call 'dots'. I am then animating the circles by rotating them with CSS3's transform, each by either 5 or 15 degrees (alternating) more than the last starting with the middle circle not rotating at all. This alternating of degrees is due to the original offset of half of them of 5deg

The animation itself works great, but the offset between each dot in a circle is not consistent. This is made obvious when the animation completes, some dots jump back. If they were all off by a consistent amount then it would be an error in my calculations, but dots around the same circle jump different amounts, meaning that they are offset different amounts to begin with. Vals, in his example at the end of his answer, also shows this inconsistency in offset

Here is how each circle is set up. The spacing in between each dot was determined by using the formula spacing = (radius × 2) × 3.14159265 ÷ numberOfCircles. The .001 is to allow Chrome to see the dots

<circle cx="30" cy="30" r="radius" stroke-dasharray="0.001, spacing" stroke="color"/>

Here is the demo jsFiddle

Can anyone help me fix this SVG rendering offset bug?

EDIT

vals and squeamish ossifrage both provided wonderfully working alternative solutions to the problem. However, I'm still looking to actually fix the offset/rendering issue if such a thing is possible

like image 479
Zach Saucier Avatar asked Dec 05 '13 23:12

Zach Saucier


People also ask

What does stroke-dasharray mean?

The stroke-dasharray attribute is a presentation attribute defining the pattern of dashes and gaps used to paint the outline of the shape; Note: As a presentation attribute, stroke-dasharray can be used as a CSS property.

What is stroke-dashoffset?

The stroke-dashoffset attribute is a presentation attribute defining an offset on the rendering of the associated dash array. Note: As a presentation attribute stroke-dashoffset can be used as a CSS property. You can use this attribute with the following SVG elements: <altGlyph> <circle>


Video Answer


1 Answers

I think that there are 2 slight mistakes in your setup.

The first is that the spacing of your dots is the sum of the 2 parameters to the stroke-dash array. Since the first parameter is always 0.001, the second one should be the result of your formula minus 0.001.

The second one is that you are placing 36 dots around the circle. That gives 10 degrees for the angle from dot to dot. So, your animations should specificy 10deg, 20deg, 30deg for the series, and not 15deg 30deg 45deg ... That creates a jump of 5 deg at the end of every cycle.

I think that I have it more or less working

fiddle

There was also an issue with the initial rotation; I hope that now it is what you wanted.

And also, there was some kind of round up due to the small size of the svg; setting it to 600 square works much better.

I have also added a line at 10 deg to check the correct alignment of the dots.

CSS

body {
    background: black;
    padding: 0;
    margin: 0;
}
circle {
    fill: none;             
    stroke-width: 10;
    stroke-linecap: round;
}
circle { -webkit-transform-origin: center center; -moz-transform-origin: center center; transform-origin: center center;
  -webkit-animation-duration: 3s; 
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-iteration-count: infinite;}
circle:nth-child(2)  { -webkit-animation-name:second; 
                       -moz-animation:second  3s ease-in-out infinite; 
                       animation:second  3s ease-in-out infinite;}
circle:nth-child(3)  { -webkit-animation-name:third; -moz-animation:third   3s ease-in-out infinite; animation:third   3s ease-in-out infinite; }
circle:nth-child(4)  { -webkit-animation-name:fourth; -moz-animation:fourth  3s ease-in-out infinite; animation:fourth  3s ease-in-out infinite; }
circle:nth-child(5)  { -webkit-animation-name:fifth; -moz-animation:fifth   3s ease-in-out infinite; animation:fifth   3s ease-in-out infinite; }
circle:nth-child(6)  { -webkit-animation-name:sixth; -moz-animation:sixth   3s ease-in-out infinite; animation:sixth   3s ease-in-out infinite; }
circle:nth-child(7)  { -webkit-animation-name:seventh; -moz-animation:seventh 3s ease-in-out infinite; animation:seventh 3s ease-in-out infinite; }
circle:nth-child(8)  { -webkit-animation-name:eighth; -moz-animation:eighth  3s ease-in-out infinite; animation:eighth  3s ease-in-out infinite; }
circle:nth-child(9)  { -webkit-animation-name:ninth; -moz-animation:ninth   3s ease-in-out infinite; animation:ninth   3s ease-in-out infinite; }
circle:nth-child(10) { 
    -webkit-animation-name:tenth; 
    -moz-animation:tenth   3s ease-in-out infinite; 
    animation:tenth   3s ease-in-out infinite; 
    -webkit-transform: rotate(10deg);}
@-webkit-keyframes second {   0% { -webkit-transform:rotate(5deg)  }
                            100% { -webkit-transform:rotate(15deg) } }
@-webkit-keyframes third {  100% { -webkit-transform:rotate(20deg) } }
@-webkit-keyframes fourth  {  0% { -webkit-transform:rotate(5deg)  }
                            100% { -webkit-transform:rotate(35deg) } }
@-webkit-keyframes fifth {  100% { -webkit-transform:rotate(40deg) } }
@-webkit-keyframes sixth   {  0% { -webkit-transform:rotate(5deg)  }
                            100% { -webkit-transform:rotate(55deg) } }
@-webkit-keyframes seventh {100% { -webkit-transform:rotate(60deg) } }
@-webkit-keyframes eighth  {  0% { -webkit-transform:rotate(5deg)  }
                            100% { -webkit-transform:rotate(75deg) } }
@-webkit-keyframes ninth  {   0% { -webkit-transform:rotate(0deg)  }
                            100% { -webkit-transform:rotate(80deg) } }
@-webkit-keyframes tenth  {   0% { -webkit-transform:rotate(5deg)  }
                            100% { -webkit-transform:rotate(95deg) } }
@-moz-keyframes second  {   0% { -moz-transform:rotate(5deg)  }
                          100% { -moz-transform:rotate(15deg)  } }
@-moz-keyframes third   { 100% { -moz-transform:rotate(20deg)  } }
@-moz-keyframes fourth  {   0% { -moz-transform:rotate(5deg)  }
                          100% { -moz-transform:rotate(35deg)  } }
@-moz-keyframes fifth   { 100% { -moz-transform:rotate(40deg)  } }
@-moz-keyframes sixth   {   0% { -moz-transform:rotate(5deg)  }
                          100% { -moz-transform:rotate(55deg)  } }
@-moz-keyframes seventh { 100% { -moz-transform:rotate(60deg)  } }
@-moz-keyframes eighth  {   0% { -moz-transform:rotate(5deg)  }
                          100% { -moz-transform:rotate(75deg) } }
@-moz-keyframes ninth   { 100% { -moz-transform:rotate(80deg) } }
@-moz-keyframes tenth   {   0% { -moz-transform:rotate(5deg)  }
                          100% { -moz-transform:rotate(95deg) } }

line {
    stroke-width: 1;
    -webkit-transform-origin: left center;
    -webkit-transform: rotate(-10deg);
}

And also optimized a little bit the styles

Well, after a lot of time spent with that issue, I am almost sure that there is some kind of bug in some kind of rounding / precision.

I have changed the idea fully to avoid this issue. The target will be to have the circles make full circles before ending the animation, so that the beginning and the end of the animation are always in sync.

Since that generates an huge keyframes style, I want to reuse it; to achieve this I have grouped the circles in a nested way; and applied the animation to every group:

HTML

<svg viewBox="0 0 60 60">
    <g class="g">
    <circle cx="30" cy="30" r="10" stroke-dasharray="0.001, 1.745" stroke="hsl(120, 100%, 50%)"/>
    <g class="g">
    <circle cx="30" cy="30" r="12" stroke-dasharray="0.001, 2.094" stroke="hsl(108, 100%, 50%)" class="c2"/>
    <g class="g">
    <circle cx="30" cy="30" r="14" stroke-dasharray="0.001, 2.443" stroke="hsl(96, 100%, 50%)"/>
    <g class="g">
    <circle cx="30" cy="30" r="16" stroke-dasharray="0.001, 2.793" stroke="hsl(84, 100%, 50%)"  class="c2"/>
    <g class="g">
    <circle cx="30" cy="30" r="18" stroke-dasharray="0.001, 3.142" stroke="hsl(72, 100%, 50%)"/>
    <g class="g">
    <circle cx="30" cy="30" r="20" stroke-dasharray="0.001, 3.491" stroke="hsl(60, 100%, 50%)" class="c2"/>
    <g class="g">
    <circle cx="30" cy="30" r="22" stroke-dasharray="0.001, 3.840" stroke="hsl(48, 100%, 50%)"/>
    <g class="g">
     <circle cx="30" cy="30" r="24" stroke-dasharray="0.001, 4.189" stroke="hsl(36, 100%, 50%)"  class="c2"/>
    <g class="g">
    <circle cx="30" cy="30" r="26" stroke-dasharray="0.001, 4.538" stroke="hsl(24, 100%, 50%)"/>
    <g class="g">
    <circle cx="30" cy="30" r="28" stroke-dasharray="0.001, 4.887" stroke="hsl(12, 100%, 50%)"  class="c2"/>
    </g></g></g></g></g></g></g></g></g></g>
</svg>

(yes, back to the low resolution !)

CSS

body {
    background: black;
    padding: 0;
    margin: 0;
}

circle {
    fill: none;             
    stroke-width: 1;
    stroke-linecap: round;
}

.g { 
    -webkit-transform-origin: center center; -moz-transform-origin: center center;                                         transform-origin: center center;
    -webkit-animation-duration: 108s; 
    -webkit-animation-timing-function: ease-in-out;
    -webkit-animation-iteration-count: infinite; 
    -webkit-animation-name: anim; 
    -moz-animation:second  3s ease-in-out infinite; 
     animation:second  3s ease-in-out infinite;}

.c2 {
    -webkit-transform-origin: center center;
    -webkit-transform: rotate(5deg); 
} 

@-webkit-keyframes anim {   0% { -webkit-transform:rotate(0deg)}
                        2.778% { -webkit-transform:rotate(10deg)}
                        5.56% { -webkit-transform:rotate(20deg)}
                        8.33% { -webkit-transform:rotate(30deg)}
                       11.11% { -webkit-transform:rotate(40deg)}
                       13.89% { -webkit-transform:rotate(50deg)}
                       16.67% { -webkit-transform:rotate(60deg)}
                       19.44% { -webkit-transform:rotate(70deg)}
                       22.22% { -webkit-transform:rotate(80deg)}
                       25% { -webkit-transform:rotate(90deg)}
                       27.78% { -webkit-transform:rotate(100deg)}
                       30.56% { -webkit-transform:rotate(110deg)}
                       33.33% { -webkit-transform:rotate(120deg)}
                       36.11% { -webkit-transform:rotate(130deg)}
                       38.89% { -webkit-transform:rotate(140deg)}
                       41.67% { -webkit-transform:rotate(150deg)}
                       44.44% { -webkit-transform:rotate(160deg)}
                       47.22% { -webkit-transform:rotate(170deg)}
                       50%    { -webkit-transform:rotate(180deg)}
                       52.78% { -webkit-transform:rotate(190deg)}
                       55.56% { -webkit-transform:rotate(200deg)}
                       58.33% { -webkit-transform:rotate(210deg)}
                       61.11% { -webkit-transform:rotate(220deg)}
                       63.89% { -webkit-transform:rotate(230deg)}
                       66.67% { -webkit-transform:rotate(240deg)}
                       69.44% { -webkit-transform:rotate(250deg)}
                       72.22% { -webkit-transform:rotate(260deg)}
                       75%    { -webkit-transform:rotate(270deg)}
                       77.78% { -webkit-transform:rotate(280deg)}
                       80.56% { -webkit-transform:rotate(290deg)}
                       83.33% { -webkit-transform:rotate(300deg)}
                       86.11% { -webkit-transform:rotate(310deg)}
                       88.89% { -webkit-transform:rotate(320deg)}
                       91.67% { -webkit-transform:rotate(330deg)}
                       94.44% { -webkit-transform:rotate(340deg)}
                       97.22% { -webkit-transform:rotate(350deg)}
                     100%     { -webkit-transform:rotate(360deg)}
}

And new demo (Sorry, only webkit)

This is my attempt to investigate the errors. I have changed the system, instead of animation, I have 2 sets of circles, one in color and another in black over it, and rotated 10 deg. The color circles shouldn't show; the offset is a measure off the error. (may be you need to scroll to see the circles

offsets demo

like image 87
vals Avatar answered Sep 29 '22 16:09

vals