寥寥数行SVG实现圆环loading或倒计时效果

这篇文章发布于 2015年07月31日,星期五,22:20,归类于 SVG相关。 阅读 73053 次, 今日 20 次 28 条评论

 

一、无题

如题。

于2017年11月2日新增案例

实际项目开发时候,设计师设计的还原可能不是下面这种纯色的圆环,而是一种复合渐变效果,类似下图:

彩条圆环

也是可以实现的,原理是使用两个渐变半圆无缝叠加在一起就可以了。

完整案例(含10秒倒计时JS代码),可狠狠地点击这里体验:SVG与多彩渐变圆环倒计时效果demo

其中SVG中使用了3个<circle>,如下截图:
3个圆环元素

分别表示底部灰色完整圆环,左半渐变圆环和右半渐变圆环。

二、历史正文

设计师设计了一个图片上传圆环loading进度效果。如下截图:
loading效果截图

首先,CSS3是可以实现的,以前写过一篇转大饼的文章:“CSS3实现鸡蛋饼饼状图loading等待转转转”。原理跟这个一模一样,两个半区的矩形,然后不同时机巧妙显隐实现。

但是呢,CSS3实现不太好理解,进度控制也不容易,最好直接数值变一变,进度效果就出来。

有没有其他方法呢?

哈,当然有,可以使用同样IE9+支持的SVG.

我们只需要一个实线背景圆,一个虚线变化圆两个圆就可以了。

百闻不如一见,您可以狠狠地点击这里:SVG实现圆环loading进度效果demo

拖动demo下面的range区域选择控件(0-100%)就会发现蓝色高亮圆环跟着一起变化了:
圆环效果截图

SVG代码非常简单,如下示意:

<svg width="440" height="440">
    <circle cx="220" cy="220" r="170" stroke-width="50" stroke="#D1D3D7" fill="none"></circle>
    <circle cx="220" cy="220" r="170" stroke-width="50" stroke="#00A5E0" fill="none" transform="matrix(0,-1,1,0,0,440)" stroke-dasharray="0 1069"></circle>
</svg>

其中,上面红色加粗的就是效果实现的关键。

stroke-dasharray在SVG中表示描边是虚线,两个值,第一个是虚线的宽度,第二个是虚线之间的间距。下面就是一些虚线数据值以及最后的效果表现(实时,非截图,IE9+浏览):

<?xml version="1.0"?>
<svg width="200" height="200" viewPort="0 0 200 300" version="1.1" xmlns="http://www.w3.org/2000/svg">

    <line stroke-dasharray="5, 5"              x1="10" y1="10" x2="190" y2="10" />
    <line stroke-dasharray="5, 10"             x1="10" y1="30" x2="190" y2="30" />
    <line stroke-dasharray="10, 5"             x1="10" y1="50" x2="190" y2="50" />
    <line stroke-dasharray="5, 1"              x1="10" y1="70" x2="190" y2="70" />
    <line stroke-dasharray="1, 5"              x1="10" y1="90" x2="190" y2="90" />
    <line stroke-dasharray="0.9"               x1="10" y1="110" x2="190" y2="110" />
    <line stroke-dasharray="15, 10, 5"         x1="10" y1="130" x2="190" y2="130" />
    <line stroke-dasharray="15, 10, 5, 10"     x1="10" y1="150" x2="190" y2="150" />
    <line stroke-dasharray="15, 10, 5, 10, 15" x1="10" y1="170" x2="190" y2="170" />
    <line stroke-dasharray="5, 5, 1, 5"        x1="10" y1="190" x2="190" y2="190" />

<style><![CDATA[
line{
    stroke: black;
    stroke-width: 2;
}
]]></style>
</svg>

之前写了篇“纯CSS实现帅气的SVG路径描边动画效果”,就是stroke-dasharray的妙用。

这里也是类似原理。

理解了stroke-dasharray,我们的事情就简单了,我们只需要让间距永远不小于圆的周长,然后,虚线的长度 = 百分比值 * 圆的周长就可以了。

简单,超乎想象:

// 假设周长是1068, percent是百分比值
circle.setAttribute('stroke-dasharray', 1068 * percent + " 1069");

就OK了。

哦~~差点忘了。默认stroke-dasharray的起始位置在右侧,而不是上方,因此,偶们需要使用transform逆时针旋转90°, 这就是第二个circle元素上面transform="matrix(0,-1,1,0,0,440)"的由来。

更新于2016年11月24日
测试发现,iOS 手机上,貌似transform逆时针旋转90°只会改变图形外观,但是其坐标起始位置还是右侧。因此,更改起始坐标可能还需要通过外部容器旋转控制。

更新于2017年11月1日
感谢辉辉的反馈,iOS手机上起始坐标异常问题,可以通过添加如下CSS修正:

svg {
  -webkit-transform: rotate(-0.05deg);
  transform: rotate(-0.05deg);
}

三、结语

目前越来越多移动端图标展示需求,很多开源图表框架的问题在于,体积大,UI很难改变,无法满足设计需求。且一般移动端的展示都比较简单,此时,使用简单的SVG实现是性价比最高的方式。

所以,好好学学SVG,比追逐热点更受益。

(本篇完)

分享到:
×


发表评论(目前28 条评论)

  1. 小熊说道:

    这里的周长怎么计算,这个半径不太对。

  2. 小米说道:

    要让间距永远不小于圆的周长 上面又写了周长*(1-百分比)。这个样子的话 那间距乘以一个比1小的数 永远小于本身 也就是周长啊,这不是矛盾吗,本来这里有点迷糊,这下子更迷糊了,求解答~

  3. sdfsdfds说道:

    第一幅图的js地址在哪啊

  4. 辉辉说道:

    针对iOS控制起始位置从正午12点的位置,本人多次测试,下面的方法有效.同时兼容android.
    .test {
    -webkit-transform: rotate(-0.05deg);
    transform: rotate(-0.05deg);
    transform-origin:center center;
    }
    希望帮到遇到同样问题的朋友!

  5. 小胖子阿杜说道:

    transform不生效时,加一度就可以了 rotate(-91deg)

  6. zhangbihua说道:

    svg viewBox属性必须大写,要不然无法随父级大小变化,不要问我怎样知道的,坑了太久时间

  7. Mr.Chen说道:

    回复:更新于2016年11月24日
    测试发现,iOS 手机上,貌似transform逆时针旋转90°只会改变图形外观,但是其坐标起始位置还是右侧。因此,更改起始坐标可能还需要通过外部容器旋转控制。

    这个是错的,你把外部容器旋转90°也没用,可用的方法为:

    -webkit-transform: rotate(-89.95deg);
    transform: rotate(-89.95deg);
    transform-origin:center center;

    为什么90°的时候不起作用,而89.95°却有用,谁能解释下?

  8. meteor说道:

    这个圆形可以,不过没有箭头不直观,需要加上箭头和百分比就好了。就是在动画跑的时候加上箭头方向(转动的方向)

  9. beryl说道:

    谢谢大师,做app下载的时候参考了大师的loading效果。发现起始位置从最右侧变到最上方可以用stroke-dashoffset这个属性。

  10. 部分手机上兼容问题说道:

    Hi, 亲, 发现当为 stroke-dasharray:0 2*Math.PI*R 时会出现有一条线,
    解决办法:特殊判断下 当为0 时,让第二个circle 标签不出现。
    http://jsbin.com/merifabefo/edit?html,js,console,output

  11. 小满说道:

    张大师,有一段代码按照你写的例子,在ios10测试上面不兼容,ios7,8,9都是正常的,主要是无法旋转,transform不起作用,麻烦看一下,能不能邮箱回复我一下,很着急,谢谢

    .test {
    -webkit-transform-origin: 50% 50%;
    transform-origin: 50% 50%;
    -webkit-transform: rotate(270deg);
    transform: rotate(270deg);
    }

  12. angus说道:

    IOS 中设置默认stroke-dasharray的起始位置在上方,可是没有效果,结果还是在默认的右方,android就没有问题

    • 部分手机上兼容问题说道:

      是的, 我也发现了这个问题,解决办法是起始位置在上方稍微给点偏差。
      .test {
      -webkit-transform-origin: 50% 50%;
      transform-origin: 50% 50%;
      -webkit-transform: rotate(269deg);
      transform: rotate(269deg);
      }

  13. Wendy说道:

    大师,请问第二个circle元素旋转时圆心是什么啊?用rotate可以么?可是我试过之后不可以,不知道为什么,麻烦大师解答。

  14. frostbelt说道:

    您好,我尝试将这个环形进度封装成一个组件,在页面中显示下载进度。
    如果页面中只有一个应用,是可以的。
    但在一个应用列表页,如何才能做到各个应用的下载进度互不影响呢?
    望回复,谢谢

  15. sciencelu说道:

    在iphone的safari中,translate旋转进度条的开始位置不能生效。

    • guanyx说道:

      transform=”matrix(0,-1,1,0,0,440)”
      这一句在Safari中不生效
      改成rotate(-90, 220, 220)就可以了

  16. 祝梓毅说道:

    在手机上显示不一样,蓝色圈圈开始的起点是下午3点,而不是正午12点

  17. luobotang说道:

    很不错的样子!
    建议稍加封装,作为开源的组件发布出来,那样大家用起来岂不是更爽!

  18. nickro说道:

    请问这个圆环能做成渐变的吗,搜了stroke没找到可以设置渐变的方法,求指教,谢谢

  19. qinqing说道:

    楼主文章中说:
    // 假设周长是1068, percent是百分比值
    circle.setAttribute(‘stroke-dasharray’, 1068 * percent + ” 1069″);
    但demo里的代码却是:
    circle.setAttribute(‘stroke-dasharray’, perimeter * percent + ” ” + perimeter * (1- percent));

    一开始我一头雾水,stroke-dasharray的最后一个值完全没有必要设置成动态的吧?不过这两个都可以实现效果。

  20. 箭头向下说道:

    不错

  21. xiaomiao说道:

    帅,一直不知道怎么做的之前。