css3
首先摆上那个灰色的圈
然后盖上一个蓝色的半圈(由一半的蓝色和一半的透明border组成)
让它转起来
把右边给他遮住,这样他还没到百分之五十的时候,右边不会出现这个半圆的一个部分
动起来是这样的
然后右边我们如法炮制一个,这样整个圆就实现了
<template>
<div class="processCircle">
<div class="grayCircle center">
<p>{{ percent }}%</p>
</div>
<section class="half left">
<div class="blueCircle"></div>
</section>
<section class="half right">
<div class="blueCircle"></div>
</section>
</div>
</template>
<script>
export default {
name: 'processCircle',
data() {
return {
percent: 1,
timeLimit: 5000
};
},
created() {
let gap = this.timeLimit / 100;
let clock = setInterval(() => {
if (this.percent < 99) {
let res = Math.floor(this.percent + 4);
this.percent = res >= 99 ? 99 : res;
if (this.percent > 40) {
gap = 0.5;
}
} else {
clearInterval(clock);
}
}, 100);
}
};
</script>
<style lang="scss">
.processCircle {
$maxl: 110px;
position: relative;
width: $maxl;
height: $maxl;
display: flex;
align-items: flex-end;
.half {
width: 51%;
height: 100%;
overflow: hidden;
position: absolute;
top: 0;
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
p {
text-align: center;
margin: auto;
color: #384369;
font-size: 18px;
font-family: fzlt_bold;
}
}
@mixin circle($w) {
width: $w;
height: $w;
$wh: $w / 2;
border-radius: $wh;
}
.grayCircle {
@include circle(106px);
border: 0;
border: 6px solid #f4f4f4;
}
.blueCircle {
// border-image: linear-gradient(to right, #90b2ff, #6d91ff) 1 10;
@include circle($maxl);
border: 10px solid transparent;
}
.left {
left: 0;
.blueCircle {
border-top: 10px solid #6d91ff;
border-left: 10px solid #6d91ff;
transform: rotate(135deg);
animation: load_left 2s ease-in;
animation-fill-mode: forwards;
}
}
.right {
right: 0;
.blueCircle {
transform: rotate(135deg);
position: relative;
left: -$maxl / 2;
animation: load_right 3s ease-in;
animation-fill-mode: forwards;
}
}
@-webkit-keyframes load_left {
0% {
border-top: 10px solid #6d91ff;
border-left: 10px solid #6d91ff;
transform: rotate(150deg);
}
50% {
border-top: 10px solid #6d91ff;
border-left: 10px solid #6d91ff;
transform: rotate(315deg);
}
100% {
border-top: 10px solid #6d91ff;
border-left: 10px solid #6d91ff;
transform: rotate(315deg);
}
}
@-webkit-keyframes load_right {
0% {
transform: rotate(135deg);
}
50% {
border-bottom: 10px solid #90b2ff;
border-right: 10px solid #90b2ff;
transform: rotate(135deg);
}
100% {
border-bottom: 10px solid #6d91ff;
border-right: 10px solid #6d91ff;
transform: rotate(312deg);
}
}
.success {
width: 90px;
height: 90px;
position: relative;
margin: 0 auto;
img {
width: 100%;
height: auto;
}
}
}
</style>
!!
但是这个效果和最终设想的差别有比较大的距离。如果使用这个方案是做不出渐变色和圆角的效果的。
svg
只要去学习一下svg的圆和渐变,就能很容易地做出来这个效果。
所以在这里简单说一下动画的原理。
在
circle
标签上支持两个属性,stroke-dashoffset
和stroke-dasharray
。
stroke-dashoffset
属性指定了dash模式到路径开始的距离
如果使用了一个 <百分比> 值, 那么这个值就代表了当前viewport的一个百分比。
属性stroke-dasharray
可控制用来描边的点划线的图案范式。
作为一个外观属性,它也可以直接用作一个CSS样式表内部的属性。
噫,老实说没看懂这两个参数到底是干啥的。动手试一下这两个属性。
控制变量法:
- 我的圆周长大约为314。
此时设置
stroke-dashoffset: 0;
stroke-dasharray: 314;
则圆为这样
- 设置
stroke-dashoffset: 0;
stroke-dasharray: 298;
合理地猜测,stroke-dasharray的改变可以变更轨迹的样子。
其实如果把这个值变小,这个外围就是虚线,所以仅仅靠变更这个值去实现连续的蓝色弧线是行不通的。
- 变更stroke-dashoffset
stroke-dashoffset: 314;
stroke-dasharray: 314;
如果这两值相等,则蓝色的部分就消失了。
stroke-dashoffset: 100;
stroke-dasharray: 314;
终上所述,设置
stroke-dasharray
为圆周长,让stroke-dashoffset
从大到小变化,则动画就会从缺到完整那么去动。
我这里设置了最终为有缺口的99%状态,效果及源码如下:
完整代码如下
// progressBar.vue
<template>
<div class="loadingContainer">
<svg
:width="progressBarParams.boxWidth"
:height="progressBarParams.boxHeight"
:VIEWBOX="
'0 0 ' +
progressBarParams.boxWidth +
' ' +
progressBarParams.boxHeight
"
id="svg"
>
<defs>
<linearGradient id="cirGradient">
<stop
offset="0%"
:stop-color="progressBarParams.frontColor"
/>
<stop
offset="100%"
:stop-color="progressBarParams.endColor"
/>
</linearGradient>
</defs>
<circle
:cx="progressBarParams.boxWidth / 2"
:cy="progressBarParams.boxHeight / 2"
:r="progressBarParams.raduis"
:stroke="progressBarParams.innerClcColor"
:stroke-width="progressBarParams.innerClcWidth"
fill="none"
stroke-linecap="round"
/>
<circle
id="clc"
class="loadingProcess"
:cx="progressBarParams.boxWidth / 2"
:cy="progressBarParams.boxHeight / 2"
:r="progressBarParams.raduis"
stroke="url(#cirGradient)"
:stroke-width="progressBarParams.outerClcWidth"
fill="none"
stroke-linecap="round"
/>
</svg>
<div id="loadingPercent">
<span>{{ percent }}</span
><span style="font-size:14px;">%</span>
</div>
</div>
</template>
<script>
export default {
props: {
progressBarParams: Object
},
data() {
return {
offset: 0,
percent: 0,
strokeDasharray: 0
};
},
created() {
let gap = 3;
let clock = setInterval(() => {
if (this.percent < 99) {
let res = Math.floor(this.percent + gap);
this.percent = res >= 99 ? 99 : res;
if (this.percent > 50) {
gap = 2;
}
if (this.percent > 80) {
gap = 1.5;
}
} else {
clearInterval(clock);
}
}, 100);
},
methods: {}
};
</script>
<style lang="scss" scoped>
.loadingContainer {
position: relative;
width: 100%;
height: 110px;
font-size: 12px;
display: flex;
}
.loadingContainer span {
font-size: 18px;
font-weight: bold;
}
.loadingContainer > svg {
margin: 0 auto;
}
#loadingPercent {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: fzlt_bold;
font-size: 18px;
color: #384369;
margin-left: 3px;
margin-top: 1px;
}
$perimter: 314;
$percent: 1;
.loadingProcess {
transform-origin: center;
animation: task 5s ease-in;
animation-fill-mode: forwards;
// stroke-dashoffset: ((100 - $percent) / 100) * $perimter;
// stroke-dasharray: $perimter;
transform: rotate(95deg);
}
@-webkit-keyframes task {
0% {
$percent: 2;
stroke-dashoffset: ((100 - $percent) / 100) * $perimter;
stroke-dasharray: $perimter;
}
20% {
$percent: 30;
stroke-dashoffset: ((100 - $percent) / 100) * $perimter;
stroke-dasharray: $perimter;
}
50% {
$percent: 60;
stroke-dashoffset: ((100 - $percent) / 100) * $perimter;
stroke-dasharray: $perimter;
}
90% {
$percent: 80;
stroke-dashoffset: ((100 - $percent) / 100) * $perimter;
stroke-dasharray: $perimter;
}
100% {
// $percent: 93;
stroke-dashoffset: 32;
stroke-dasharray: 329;
}
}
</style>
// 使用
<progress-bar
v-if="status === 'loading'"
v-bind:progressBarParams="progressBarParams"
></progress-bar>
<script>
import progressBar from './progressBar';
export default {
name: 'processCircle',
components: {
progressBar
},
props: {
status: {
type: String,
default: 'loading'
}
},
data() {
return {
percent: 1,
timeLimit: 5000,
progressBarParams: {
boxWidth: '110',
boxHeight: '110',
raduis: '50',
innerClcWidth: '6',
outerClcWidth: '10',
frontColor: '#90B2FF',
endColor: '#6D91FF',
innerClcColor: '#F4F4F4',
fastDurTime: 100,
slowDurTime: 1000
}
};
},
}
</script>