最近项目中使用到一个Loading效果,其实是一个很简单的效果,主要是因为这个Loading出现在不同的场景之中,而且大小也不一致。对于这样的效果,往往都会想通过组件的方式来处理,其出发点就是更易维护,易扩展。当然,这对于前端的同学而言并没有什么复杂性,也没有多少技术含量。不过我还是希望把这个过程记录下来。
咱们先来看一个截图:
从上图可以看出来,其效果是一样的,不同之处是使用场景不同,大小不同而以。那么接下来,就来聊聊这样的一个效果怎么通过不同的方式来完成。
实现原理
实现上面的这样的一个效果,我们需要有一点点数学相关的知识,这样更有易于后续效果制作。比如说,示例中有个圆点,那么这个圆点具有自己相关的特性参数:
圆点的半径,比如说
圆点的颜色,比如说
圆点的位置,按一定的比例分布在一个容器上,并且围成一个圆形
比如图所示:
注意,上较绘制的不是很标准,只是为了阐述问题。这里将会使用到一些数学公式,因为我们需要知道每个圆点的圆心的位置。
继续简化一下,就如下图这样:
这里会运用到一些角度和弧度相关的知识,其实这部分知识点,在学习Canvas的时候有所涉猎。在CSS中,咱们做旋转一般使用的是(角度)为单位,但在JavaScript绘制圆或圆弧却常用弧度为单位。
一个完整的圆的弧度是,所以,,,(约)。以度数表示的角度,把数字乘以便转换成弧度;以弧度表示的角度,乘以便转换成度数。
rad =(π /180) * deg
同样的:
deg =(rad *180) / π
使用JavaScript来实现角度和弧度之间的换算。一个大约是,在JavaScript中对应的是。那么角度和弧度之间的换算:
rad =(Math.PI * deg) /180
同样的:
deg =(rad *180) / Math.PI
下图展示了常见的角度和弧度之间的换算:
接下来回到我们的示例中来,示例有个圆点,那么其每个圆点对应的位置可以通过下面的公式计算出来。首先计算出每个点对应的值。
rad =Math.PI * deg /180
根据上面的公式,我们需要知道。众所周知,一个圆是,我们在这个圆上平均布了个点,那么每个圆对应的值是
deg =360/14* i
其中是一个从的索引值。套到对应的公式中:
rad =Math.PI *360/14* i /180
在JavaScript中,使用一个循环,可以打印出其值:
for(let i = 0, len = 14; i
根据上面的计算得到每个圆点对应的值,接下来就需要利用三角函数相关的知识,来计算每个圆点圆心的值。
换成JavaScript中的数学公式:
dotX =Math.cos(rad) * rdotY =Math.sin(rad) * r
假设外圆的容器半径。继续将上面的值放到循环中:
for(let i =, len =14; i
最后通过CSS的中可以将个圆点围成一个圆圈。此时,如果你的效果中不是个圆点,而是五个圆点的时候,你只需要修改相关的参数即可。
圆点排列,通过上面的公式计算,已经搞定。现在是需要一个动画效果,让你的圆点变得更为有趣。也就是说,只有配上了动效,才是我们最终需要的Loadingu效果。接下来,来看一下,圆点的动效。
比如,我们有一个简单的动效,这个动效是通过来完成的。具体效果可以根据自己的需要来做,这里只是一个简单的效果:
@keyframes ball-spin{ 0%, 100%{
opacity:1;transform:scale(1);}20%{
opacity:1;}80%{
opacity:;transform:scale();}}
不用我解释,大家一看就明白。假设我们的动画的持续时间是。效果如下:
如果你要的是这样的一个效果,那到这里,理论上是完成了。可我们要的效果是这样的:
要实现这样的一个效果,我们需要对每个圆点的做一些处理。前面提到过,整个动画的是,那么每个圆点的依此延迟。如此一来,咱们可以计算出时间:
-1 * (1 + (i + 1) * 1 / 14)
注意:此处的控制的是方向,让你能感觉到loading效果是顺时针还是逆时针转。系数为表时逆时针,就是上图看到的效果。
整个运用到的原理就如上所述。接下来分别看看几种不同方式的实现。
纯CSS实现方式
Loading效果实现方式有很多种,比如@css_live整理这些Loading动效的Demo,就涵盖了多种实现方式,有纯CSS的、有Canvas的,也有SVG的。而且在CodePen上也有很多Loading的Demo或这里的Demo演示。
这里我们先用纯CSS的方式来完成。我们需要一个完成动效的HTML结构:
下的对应的个数就是你所需要的圆点数。这里使用了个。为什么要使用样的嵌套标签,简单的解释一下。因为圆点有两个效果:
位置排列,使用的是中的
动效使用的是中的
如果我们只用一个,那么圆点的排列和动效中同时使用到的较为难控制。所以为了让实现方式更简单,牺牲了结构,嵌套了一个标签。也就是排列用在上,动效用在上。
众所周知,纯CSS是不具备动态计算的能力,如果我们要实现上面的效果,就要人肉的去计算。不过值得庆幸的是,有很多优秀的CSS处理器,比如说Sass、LESS之类的都具备动态计算,也具备上面所说的数学函数和循环的特性。这样一来,事情就变得简单多了。
详细代码就不全贴了,整几个关键的代码片段。首先根Demo效果所需要的参数,声明几个变量:
$loadingSize:200px;//Loading容器的大小
$dotRadius:24px;//圆点半径$dotNums:14;//圆点个数(需要和div.loading中子元素div个数对应起来)
$dotColor:#f36; // 圆点颜色
另外循环以及圆点位置相关的代码如下:
.loading {
width:$loadingSize;
height:$loadingSize;
color:$dotColor; transform-origin:center; div {
color:$dotColor;
width:$dotRadius;
height:$dotRadius; margin-top:$dotRadius/2; margin-left:$dotRadius/2; span {
width:$dotRadius;
height:$dotRadius;
animation:ball-spin1s infinite ease-in-out; background-color:currentColor;
border:solid currentColor; }
@for$ifrom1through14{ &:nth-child(#{$i}) {transform:translate(cos(($i-1) *360deg /$dotNums) *$loadingSize/2, sin(($i-1) *360deg /$dotNums) *$loadingSize/2); & > span { animation-delay:-(1+$i*1/$dotNums) *1s } } } }}
对应一些三角函数的代码这里就不列出来了。如果你感兴趣的话可以使用sass-math。你也可以将示例中相应的函数像SassMagic一样放到一个Sass的仓库中,方便之后使用。
虽然效果出来了,但其可扩展性相对而言较为麻烦一点,比如说我要一个小一点的,个数少一点的,颜色不一样的。那么需要重新修改前面声明的变量,然后再编译出相应的代码,修改对应的HTML结构。感兴趣的同学可以试试。
CSS自定义属性
CSS自定义属性已经是很成熟的CSS属性了。在小站上也有多篇文章介绍了有关于CSS自定义属性。为什么会想到CSS自定义属性呢?因为能使用CSS处理器变量的,就可以使用CSS自定义属性。但在这里有一个蛋疼的地方,就是CSS自定义属性调用通过函数来完成,然后计算要依赖于函数。那么要把其结合Sass这样的处理器来做,就基本上是无解(至少我现在没找到可解的方案)。但CSS自定义属性有一个特性,可以使用它来完成CSS自定义属性的动态变化。
因此,使用CSS自定义属性方案来实现Loading的动画效果的时候,我借用了原生JavaScript的手段来完成圆点的排列和动画的计算。为了能更好的展示不同的Loading效果,接下来的Demo提供了一些可配置参数。这些可配置参数,让你来动态修改CSS自定义属性的值。先上效果吧:
你可以像下面这样,修改Demo右侧的一些参数,实现不一样的Loading效果,如下图所示:
为了节省篇幅,也将只贴一些关键的代码:
:root { --loadingRadius:168px; --dotRadius:24px; --dotColor:#d8d8d8;}.loading{ width:var(--loadingRadius); height:var(--loadingRadius); color:var(--dotColor); transform-origin: center; div { width:var(--dotRadius); height:var(--dotRadius); color:var(--dotColor); margin-top: calc((var(--dotRadius) /2) * -1); margin-left: calc((var(--dotRadius) /2) * -1); } span { animation: ball-spin1s infinite ease-in-out; width:var(--dotRadius); height:var(--dotRadius); }}
对应的JavaScript代码如下:
conststyle = document.documentElement.style;
varrangs = { dotNums: document.getElementById('dotNums'), loadingRadiusVal: document.getElementById('loadingRadius'), dotRadiusVal: document.getElementById('dotRadius'), dotColorVal: document.getElementById('dotColor')}
// 创建修改CSS自定义属性的函数
functionvalueChange(id, value){style.setProperty('--'+ id, value);}
// 动态创建div.loading下的子元素div+span
functioninsertHtml(){letloadingWrap = document.getElementById('loading');
vardots = rangs.dotNums.value;
for(leti =; i
letdivEle = document.createElement('div');
letspanEle = document.createElement('span'); divEle.appendChild(spanEle); loadingWrap.appendChild(divEle) } }
// 右侧参数面板的控制
rangs.loadingRadiusVal.addEventListener('input',function(e){ valueChange(e.currentTarget.id, e.currentTarget.value +'px');})rangs.dotRadiusVal.addEventListener('input',function(e){ valueChange(e.currentTarget.id, e.currentTarget.value +'px');})rangs.dotColorVal.addEventListener('input',function(e){ valueChange(e.currentTarget.id, e.currentTarget.value);})
functiontransformDot(){letdotNums = document.querySelectorAll('.loading > div');
letloadingRadiusVal = rangs.loadingRadiusVal.value;
letdotRadiusVal = rangs.dotRadiusVal.value;
letdotColorVal = rangs.dotColorVal.value;// 计算圆点的位置和动效for(leti =, len = dotNums.length; i
letrad =2* Math.PI / dotNums.length * i;
letdotX = Math.cos(rad) * loadingRadiusVal /2;
letdotY = Math.sin(rad) * loadingRadiusVal /2; dotNums[i].style.transform = `translate($px,$px)`; dotNums[i].firstElementChild.style.animationDelay = -1* (1+ (i +1) *1/ dotNums.length) +'s'; }}insertHtml();transformDot();
// 修改参数后修改动效
rangs.dotNums.addEventListener('input',function(e){
letloadingWrap = document.getElementById('loading'); loadingWrap.innerHTML =''; insertHtml(); transformDot();})
对应的HTML模板,这里就不展示了。
其实我还在想,这个效果,我们应该也可以使用CSS Houdini来完成。如果你对CSS Houdini有一定的了解,不仿尝试写写。如果你对CSS Houdini一点都不了解,建议点击这里先了解一下,我想你会从此喜欢上她的。
Vue写Loading组件
除了上面的方法之外,为了更好的使用,还可以将其写成一个组件。比如一个Vue组件,当然喜欢使用其他JavaScript框架的同学也可以使用别的,比如React。这里用的是Vue。
首先创建一个Vue组件,你可以使用单的一个文件,也可以使用组件模板。因为Demo是在Codepen上展示的,我就使用了一个组件模板:
Vue.component('loading',{ template: '#loading', props: { loadingRadiusVal: { type: Number, required: true, default: 168 }, dotRadiusVal: { type: Number, required: true, default: 24 }, dotColorVal: { type: String, required: true, default: '#d8d8d8' }, dotNums: { type: Number, required: true, default: 10 } }, methods: { dotTransform: function(index, dotNums) { let rad = 2 * Math.PI / dotNums * index; let dotX = Math.cos(rad) * this.loadingRadiusVal / 2; let dotY = Math.sin(rad) * this.loadingRadiusVal / 2; return { transform: `translate($px,$px)` }; }, dotAimation: function(index, dotNums) { let delayTime = `${-1 * (1 + (index + 1) * 1 / dotNums) }s` return { animationDelay: delayTime } } } })
有了这个组件的时候,咱们可以这样调用:
let app = newVue({el:'#app', data () {
return{
loadingRadius:168,
dotRadius:20,
dotColor:'#ff3366',
dotNums:12} },
computed:{
changeStyle:function() { let rootEle = document.documentElement; rootEle.style.setProperty('--loadingRadius', `${this.loadingRadius}px`) rootEle.style.setProperty('--dotRadius', `${this.dotRadius}px`) rootEle.style.setProperty('--dotColor', this.dotColor) } }})
和前面的示例一样,也提供了一个参数控制面板,不过在Vue中参数控制面板控制CSS自定义属性就容易的多了,采用的双向绑定即可。最终效果如下:
因为是Vue的初学者,如果写得不好,还请各咱大神多多指正。如果你也是Vue的爱好者或初学者,可以和我一起来学习Vue相关的知识。当然,Vue写的各式各样的Loading组件非常的多,比如这里就收集了很多。
总结
这篇文章主要介绍了如何使用不同的方式来实现一个Loading的动效。说实在的,前端就是这样,实现一个效果有很多种方式方法。不同层次的同学可以使用不同的方式。就好比这篇文章中介绍的。不懂JavaScript的可以使用纯CSS(最好你对CSS处理器有所了解),懂JavaScript的话,你可以使用JS,配合一些优秀的CSS特性。当然,为了更易于维护和扩展,也可以借助Vue这样的框架,将整个效果封装成一个组件。
那么整个效果的实现方式不同,但其原理是一样的。对于初学者而言,最关键的是要掌握原理。只有掌握了原理部分,你才能根据自己的环境选择实现方案。
如果上面有不对之处,或者你有更好的方案,欢迎在下面的评论中与我一起共享。如果这篇文章对你有所帮助,可以赏杯咖啡,鼓励我继续创作。(^_^)!!!
领取专属 10元无门槛券
私享最新 技术干货