SVG超简单实现豆瓣loading动画

8,676 阅读6分钟

这段时间在豆瓣上看有什么剧的时候,发现豆瓣的loading动画还挺棒的。所以决定模仿着来写一个。

效果图

我通过CSS3配合SVG来完成这次模仿豆瓣的loading动画。没用过SVG的也不用慌,我下面会写出相应的属性方便你去理解。因为这个真的超简单。那么我们就直奔主题吧

初始化svg

我们需要先完成一个静态的笑脸

始化SVG视图的大小

<svg width="100" height="100"></svg>

我指定了一个宽高都为100px的区域,width=”100”width=”100px”是等价的。

画嘴巴

我们把嘴巴给画出来,在svg内先画一个圆

<circle class="mouth" cx="50" cy="50" r="14"></circle>

cxcy 属性定义圆点的 xy 坐标。

看到浏览器上面多了一个填充的黑色圆点

原型

接下来我们给我们的嘴巴加上css样式

.mouth {
    fill: none;
    stroke: #00B51D;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-dasharray: 44, 44;
    transform-origin: center;   /* transform动画时以自身中心作为基点 */
}

对应的属性

css属性 说明 此处
fill 填充颜色 none(不填充)
stroke 轮廓的颜色 #00B51D
stroke-width 轮廓的宽度 5
stroke-linecap 开放路径两端的形状 round
stroke-dasharray 创建虚线 44, 44
transform-origin 旋转元素的基点位置 center(自身中心)

这里重点讲一下stroke-dasharray,它的属性值可为none<dasharray>inherit

<dasharray>它是一个<length><percentage>数列,数与数之间用逗号或者空白隔开,指定短划线和缺口的长度。

一个参数时: 其实是表示虚线长度和每段虚线之间的间距
两个参数或者多个参数时:一个表示长度,一个表示间距

  • stroke-dasharray = '10' 表示:虚线长10,间距10,然后重复 虚线长10,间距10
  • stroke-dasharray = '10, 5' 表示:虚线长10,间距5,然后重复 虚线长10,间距5
  • stroke-dasharray = '20, 10, 5' 表示:虚线长20,间距10,虚线长5,接着是间距20,虚线10,间距5,之后开始如此循环

根据圆的周长公式L=2πr,圆的半径r=14,所以L=87.96452≈88,那么我们取虚线长度为44,间距44做一条虚线,刚好就是圆周长的一半。

可见此时我们的嘴巴部分已经完成了

原型

画眼睛

接下来开始画眼睛,我又画了一个circle

<circle class="eye" cx="50" cy="50" r="14"></circle>

原型

我们依然可以通过stroke-dasharray来画出一对眼睛

先把嘴巴注释掉,避免影响我们调试。

stroke-dasharray的虚线长度为0的时,则可以得到无数个点状的圆。根据上面圆的周长公式我们可以得到周长为88,那么我们每1/4个长度就为22,此时我们再把间距调整为66。此时两个点的垂直距离刚好为90度

接下来我们给我们的眼睛加上css样式

.eye {
    fill: none;
    stroke: #00B51D;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-dasharray: 0, 66;
}

原型

为了让嘴巴对齐,我们左旋转45度,给eye加上transform属性

transform-origin: center;
transform: rotate(-45deg);

原型

这样子我们就完成了眼睛

加动画

经过我无数遍慢放(一帧一帧)的观察豆瓣的加载动画,终于找到了规律(哭)。

嘴巴动画

加动画效果,这也是最激动的一步,我们先来看下嘴巴部分

原型

通过动画我们可以看出嘴巴部分是旋转了两圈的,并且在旋转第一圈的时候,两边的间距缩小为1/4,在第二圈的时候,间距恢复为1/2,且最后有一段的停留时间

根据最后的一段停留时间旋转两圈

我们可以通过transform完成,简单

@keyframes mounthAni {
    80%, 100% {
        transform: rotate(720deg);
    }
}

第一圈的时候两边间距缩小为1/4,也就是此时stroke-dasharray的值为 44, 22,后续再恢复为1/2

@keyframes mounthAni {
    40% {
        stroke-dasharray: 44, 22;   /* 间距改为1/4 */
    }
    80%, 100% {
        stroke-dasharray: 44, 44;   /* 间距恢复为1/2 */
        transform: rotate(720deg);
    }
}

动画的运行速度为开始快,结束慢,所以我们使用ease-out作为贝塞尔曲线函数值

这样嘴巴的动画就完成了

眼睛动画

原型

同样的,通过动画我们可以看出眼睛部分同样是旋转了两圈,并且在旋转第一圈的时候,两边的间距增大为7/8,在第二圈的时候,间距恢复为3/4,且最后有一段的停留时间

@keyframes eyeAni {
    40% {
        stroke-dasharray: 0, 77;    /* 间距改为7/8 */
    }
    80%, 100% {
        transform: rotate(675deg);  /* 间距恢复为3/4 */
        stroke-dasharray: 0, 66;
    }
}

动画的运行速度为开始和结束慢,所以我们使用ease-in-out作为贝塞尔曲线函数值

至此我们的眼睛动画也就完成了

我们把两个动画整合起来,这样就完成了模仿豆瓣的loading动画了,特简单。

效果图

源码

html

<svg width="100" height="100">
    <circle class="mouth" cx="50" cy="50" r="14"></circle>
    <circle class="eye" cx="50" cy="50" r="14"></circle>
</svg>

css

.mouth {
    fill: none;
    stroke: #00B51D;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-dasharray: 44, 44;
    transform-origin: center;   /* transform动画时以自身中心作为基点 */
    animation: mounthAni 2.3s ease-out infinite;
}

.eye {
    fill: none;
    stroke: #00B51D;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-dasharray: 0, 66;
    transform-origin: center;
    transform: rotate(-45deg);
    animation: eyeAni 2.3s ease-in-out infinite;
}

@keyframes mounthAni {
    40% {
        stroke-dasharray: 44, 22;   /* 间距改为1/4 */
    }
    80%, 100% {
        stroke-dasharray: 44, 44;   /* 间距恢复为1/2 */
        transform: rotate(720deg);
    }
}

@keyframes eyeAni {
    40% {
        stroke-dasharray: 0, 77;    /* 间距改为7/8 */
    }
    80%, 100% {
        transform: rotate(675deg);  /* 间距恢复为3/4 */
        stroke-dasharray: 0, 66;
    }
}

总结

CSS3动画已足够强大,但是如果要实现上面那种间距可变大变小的动画效果,只用CSS的话我还是没有想到怎么实现,如果各位有方法的话欢迎评论告诉我😀,所以使用了SVGCSS来完成,眼睛部分原本想过使用animateMotion来完成,奈何看了半天还是不会用🤣,所以就想着只用CSS的动画效果完成了这次的动画,没想到意外的简单,所以把方法分享给大家。整个loading动画都围绕着stroke-dasharray展开,用到的SVG属性真的是冰山一角,上面的内容如果有什么错误的话还望各位多多指教。