美文网首页前端开发那些事儿
Step by step!css3、svg 画圆形渐变圆角进度条

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

作者: 景阳冈大虫在此 | 来源:发表于2020-05-23 16:03 被阅读0次

    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>
    

    相关文章

      网友评论

        本文标题:Step by step!css3、svg 画圆形渐变圆角进度条

        本文链接:https://www.haomeiwen.com/subject/mkjkahtx.html