Step by step!css3、svg 画圆形渐变圆角进度条

css3

首先摆上那个灰色的圈



然后盖上一个蓝色的半圈(由一半的蓝色和一半的透明border组成)



让它转起来
这就像进度为百分之五十的时候的样子

把右边给他遮住,这样他还没到百分之五十的时候,右边不会出现这个半圆的一个部分



动起来是这样的


左边.gif

然后右边我们如法炮制一个,这样整个圆就实现了
完整效果
<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-dashoffsetstroke-dasharray

stroke-dashoffset 属性指定了dash模式到路径开始的距离
如果使用了一个 <百分比> 值, 那么这个值就代表了当前viewport的一个百分比。
属性stroke-dasharray可控制用来描边的点划线的图案范式。
作为一个外观属性,它也可以直接用作一个CSS样式表内部的属性。

噫,老实说没看懂这两个参数到底是干啥的。动手试一下这两个属性。

控制变量法:
  1. 我的圆周长大约为314。
    此时设置
    stroke-dashoffset: 0;
    stroke-dasharray: 314;

则圆为这样


  1. 设置
    stroke-dashoffset: 0;
    stroke-dasharray: 298;

合理地猜测,stroke-dasharray的改变可以变更轨迹的样子。
其实如果把这个值变小,这个外围就是虚线,所以仅仅靠变更这个值去实现连续的蓝色弧线是行不通的。


  1. 变更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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343