SVG描边虚线偏移量不一致。

14

我有一系列由小圆点组成的边框圆圈,我将它们称为“点”。然后使用CSS3的transform将圆圈旋转,每个圆圈的旋转角度为5度或15度(交替),从不旋转的中间圆圈开始,每个圆圈的旋转角度比上一个圆圈增加。

动画本身效果很好,但是同一圆圈内的每个点之间的偏移量不一致。当动画完成时,有些点会跳回原来的位置。如果它们都有相同的偏移量,那么这将是我的计算错误,但是在同一个圆圈内,不同的点跳跃的量不同,这意味着它们最初的偏移量也不同。Vals在他的答案最后的示例中也显示了这种偏移量的不一致性。

下面是每个圆圈的设置方式。使用公式spacing = (radius × 2) × 3.14159265 ÷ numberOfCircles确定每个点之间的间距。其中的.001是为了让Chrome能够看到这些点。

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

这里是演示 jsFiddle

有人能帮我修复这个 SVG 渲染偏移的 bug 吗?

编辑

vals 和 squeamish ossifrage 都提供了完美的替代解决方案。但是,如果可能的话,我仍然希望实际修复偏移/渲染问题。


我对这个问题的答案非常感兴趣。我尝试了各种不同的值来使用 stroke-dashoffset,但都没有成功。 - Tyler Eich
+1 是因为我被 Chrome 催眠了。 - Andrea Ligios
1
@AndreaLigios 你可能会喜欢我的其他项目 (: - Zach Saucier
在我的答案末尾添加了我最后一次尝试调查偏移量的内容;也许可以帮助某人找到解释。 - vals
2个回答

5
我认为您的设置中有两个小错误。
第一个是您的点间距是stroke-dash阵列的两个参数之和。由于第一个参数始终为0.001,因此第二个参数应该是您的公式结果减去0.001。
第二个是您在圆周上放置36个点。这给出了每个点之间10度的角度。因此,您的动画应该指定10度、20度、30度的系列,而不是15度、30度、45度... 这会在每个周期的末尾产生5度的跳跃。
我认为现在我更或多或少地解决了这个问题。
CSS fiddle 还有一个问题是初始旋转;希望现在它是您想要的。
另外,由于svg的小尺寸,可能存在某种舍入问题;将其设置为600平方效果更好。
我还添加了一条在10度处的线来检查点的正确对齐。
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);
}

此外,我还稍微优化了样式。

花费了很多时间处理这个问题后,我几乎可以确定存在某种舍入/精度错误的bug。

我已经完全改变了想法,以避免这个问题。目标是让圆圈在动画结束之前完成整个圆圈,以便动画的开始和结束始终同步。

由于这会生成一个巨大的关键帧样式,我想重复使用它;为了实现这一点,我将圆圈分组嵌套,并将动画应用于每个组:

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)}
}

这是我尝试研究错误的方式。我更改了系统,使用了两组圆圈代替动画,一组是彩色的,另一组是黑色的,它们重叠在一起并旋转10度。彩色圆圈不应该显示出来; 偏移量是误差的度量。(你可能需要滚动查看圆圈)

演示偏移量

还有一个新的演示 (抱歉,只支持webkit)


我使用你的编辑更新了JSFiddle(http://jsfiddle.net/75WsM/15/)。问题仍然存在。 - Tyler Eich
我已经更新了我的jsFiddle,展示了我想要的效果(在5度和15度旋转之间交替 - 总共10度平均变化)。正如Tyler所展示的那样,我不认为跳跃是由于0.001参数引起的。 - Zach Saucier
+1。不可否认这更接近了,但还是有一些微小的跳动(可能是由于偏移造成的)。您也注意到了吗?这不仅仅是我的问题,对吧? - Zach Saucier
我没有注意到跳跃,因为我在缩小的尺寸下测试,但是它们确实存在。添加了新的解决方案,完全绕过了这个问题。 - vals
你的编辑太棒了。对于浏览器SVG渲染引擎来说是一个极好的解决方法!我优化了你的CSS,移除了Webkit依赖:http://jsfiddle.net/zVt5x/ - Tyler Eich
@TylerEich 嘿,谢谢!昨天我时间不够了。 - vals

2
我认为问题在于虚线绘制算法使用了快速的线段长度近似值,而没有真正积分路径长度沿着每个线段,这可能会相当缓慢。
如果您将点绘制为单独的圆而不是使用虚线,则可以解决此问题。以下PHP脚本将为您执行计算:
<svg viewBox="0 0 60 60">
<g transform="translate(30,30)">
<?php
define("PI",3.141592654);
for ($i=0; $i<10; $i++) {
  $r = 10 + 2 * $i;
  $hue = 120 - 12 * $i;
  echo "<g id=\"ring_$i\">\n";
  for ($th=0; $th<360; $th+=10) {
    $theta = ($th + 5 * $i) * PI / 180;
    $x = $r * sin($theta);
    $y = $r * cos($theta);
    printf("  <circle cx=\"%.5f\" cy=\"%.5f\" r=\"0.45\" fill=\"hsl(%d,100%%,50%%)\"/>\n",$x,$y,$hue);
  }
  echo "</g>\n";
}
?></g>
</svg>

以下是CSS代码(为了简洁起见,省略了供应商特定规则):

body { background: black; padding: 0; margin: 0; }
g#ring_0 { transform:rotate(5deg); }
g#ring_1 { animation:second 3s ease-in-out infinite; }
g#ring_2 { animation:third 3s ease-in-out infinite; }
g#ring_3 { animation:fourth 3s ease-in-out infinite; }
g#ring_4 { animation:fifth 3s ease-in-out infinite; }
g#ring_5 { animation:sixth 3s ease-in-out infinite; }
g#ring_6 { animation:seventh 3s ease-in-out infinite; }
g#ring_7 { animation:eighth 3s ease-in-out infinite; }
g#ring_8 { animation:ninth 3s ease-in-out infinite; }
g#ring_9 { animation:tenth 3s ease-in-out infinite; }
keyframes second { 100% { transform:rotate(10deg) } }
keyframes third { 100% { transform:rotate(20deg) } }
keyframes fourth { 100% { transform:rotate(30deg) } }
keyframes fifth { 100% { transform:rotate(40deg) } }
keyframes sixth { 100% { transform:rotate(50deg) } }
keyframes seventh { 100% { transform:rotate(60deg) } }
keyframes eighth { 100% { transform:rotate(70deg) } }
keyframes ninth { 100% { transform:rotate(80deg) } }
keyframes tenth { 100% { transform:rotate(90deg) } }

您可以在这里看到它的运行效果:JSFiddle

那是很多圆圈,哈哈。实心的选择也不错! - Zach Saucier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接