Vue组件loading加载效果实现

摘要

对于页面加载,网络请求等待,为了增强用户体验,往往会出现一个loading加载效果,相对减少用的焦虑感,那怎样去实现这样一个组件呢,可以用css的帧动画,也可以使用canvas画出这样一个动图。

本文将以css帧动画的形式实现loading加载效果
涉及的图片形式如下


image.png

1.采用css动画完成,keyframes定义0-100%的动画区间,每一个百分比运行的距离,一步完成所有的帧,无限循环,这种太过于冗余,得不偿失,不建议使用

// loading组件
<template>
    <div class="loading-wrap" v-show="show">
        <div class="loading">
            <div class="img"></div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'ModalLoading',
    data() {
        return {
            show: false
        };
    }
};
</script>

<style lang="scss" scoped>
@keyframes anima {
    0.0000% {
        background-position: 0px 0;
    }
    2.8571% {
        background-position: -120px 0;
    }
    5.7143% {
        background-position: -240px 0;
    }
    8.5714% {
        background-position: -360px 0;
    }
    11.4286% {
        background-position: -480px 0;
    }
    14.2857% {
        background-position: -600px 0;
    }
    17.1429% {
        background-position: -720px 0;
    }
    20.0000% {
        background-position: -840px 0;
    }
    22.8571% {
        background-position: -960px 0;
    }
    25.7143% {
        background-position: -1080px 0;
    }
    28.5714% {
        background-position: -1200px 0;
    }
    31.4286% {
        background-position: -1320px 0;
    }
    34.2857% {
        background-position: -1440px 0;
    }
    37.1429% {
        background-position: -1560px 0;
    }
    40.0000% {
        background-position: -1680px 0;
    }
    42.8571% {
        background-position: -1800px 0;
    }
    45.7143% {
        background-position: -1920px 0;
    }
    48.5714% {
        background-position: -2040px 0;
    }
    51.4286% {
        background-position: -2160px 0;
    }
    54.2857% {
        background-position: -2280px 0;
    }
    57.1429% {
        background-position: -2400px 0;
    }
    60.0000% {
        background-position: -2520px 0;
    }
    62.8571% {
        background-position: -2640px 0;
    }
    65.7143% {
        background-position: -2760px 0;
    }
    68.5714% {
        background-position: -2880px 0;
    }
    71.4286% {
        background-position: -3000px 0;
    }
    74.2857% {
        background-position: -3120px 0;
    }
    77.1429% {
        background-position: -3240px 0;
    }
    80.0000% {
        background-position: -3360px 0;
    }
    82.8571% {
        background-position: -3480px 0;
    }
    85.7143% {
        background-position: -3600px 0;
    }
    88.5714% {
        background-position: -3720px 0;
    }
    91.4286% {
        background-position: -3840px 0;
    }
    94.2857% {
        background-position: -3960px 0;
    }
    97.1429% {
        background-position: -4080px 0;
    }
    100.0000% {
        background-position: -4200px 0;
    }
}
.loading-wrap {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 999999;
    background: rgba(24, 26, 36, 1);
    .loading {
        position: absolute;
        top: 50%;
        left: 50%;
        width: 260px;
        height: 140px;
        border-radius: 10px;
        background: rgba(0, 0, 0, 0.7);
        transform: translate3d(-50%, -50%, 0);
        text-align: center;
        line-height: 140px;

        .img {
            position: absolute;
            top: 50%;
            left: 50%;
            width: 120px;
            height: 65px;
            transform: translate3d(-50%, -50%, 0);
            background: url('./images/loading.png') no-repeat;
            background-size: 3600% 100%;
            animation: anima 2s steps(1) infinite;
        }
    }
}
</style>

2.也是采用css动画,图片有36张,就相当于是36帧,但是注意动画是从0开始的,实际看到第36张图的其实点是35的结尾,所以steps(35),keyframes定义100%的即可

<template>
    <div class="loading-wrap" v-show="show">
        <div class="loading-com loading" :class="{ 'bg-com': needBg }" v-if="true">
            <div class="img"></div>
            <p class="txt">正在加载中...</p>
        </div>
        <div v-else class="loading-com loading-other" :class="{ 'bg-com': needBg }">
            <div class="img"></div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'ModalLoading',
    data() {
        return {
            show: false,
            needBg: false
        };
    }
};
</script>

<style lang="scss" scoped>
@keyframes anima {
    100% {
        background-position: -3500px 0;
    }
}

.loading-wrap {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 999999;
    background: rgba(24, 26, 36, 1);
    .loading-com {
        position: absolute;
        top: 50%;
        left: 50%;
        width: 260px;
        height: 140px;
        border-radius: 10px;
        transform: translate3d(-50%, -50%, 0);
        text-align: center;
    }
    .bg-com {
        background: rgba(0, 0, 0, 0.7);
    }
    .loading {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;

        .txt {
            margin-top: 22px;
            font-size: 28px;
            font-weight: 400;
            color: rgba(103, 106, 125, 1);
        }
    }
    .img {
        width: 100px;
        height: 54px;
        background: url('./images/loading.png') no-repeat;
        background-size: 3600% 100%;
        animation: anima 1.5s steps(35) infinite;
    }

    .loading-other {
        height: 80px;
        width: 140px;
        .img {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate3d(-50%, -50%, 0);
        }
    }
}
</style>

3.其次就是封装成可以在任何地方调用的组件

/**
 * 使用方法
 * 显示loading 不是在实例化的组件中 Vue.showLoading()  如果是在vue的组件中使用则 this.$showLoading()
 * 关闭loading 不是在实例化的组件中 Vue.closeLoading()        如果是在vue的组件中使用则 this.$closeLoading()
 */

import ModalLoading from './ModalLoading.vue';
let ModalLoad = {};
ModalLoad.install = function(Vue) {
    // 在全局状态下只有一个实例 options = {}option = {}
    let instance;
    let defaultOptions = {
        needBg: false // 默认不需要背景
    };
    // 在Vue的原型上扩展方法
    Vue.showLoading = Vue.prototype.$showLoading = function(option) {
        defaultOptions = Object.assign(defaultOptions, option);
        // 实例化所引入的插件
        const ModalLoadController = Vue.extend(ModalLoading);
        // 判断当前实例是不是已经存在
        if (!instance) {
            // 实例化插件并挂在到某一个元素上
            instance = new ModalLoadController({
                el: document.createElement('div')
            });
            // 添加在body内
            document.body.appendChild(instance.$el);
        }
        if (instance.show) return;
        instance.show = true;
        instance.needBg = defaultOptions.needBg;
    };
    Vue.closeLoading = Vue.prototype.$closeLoading = function() {
        instance ? (instance.show = false) : '';
    };
};
// 自动安装插件
if (typeof window != 'undefined' && window.Vue) {
    // eslint-disable-next-line no-undef
    Vue.use(ModalLoad);
}
// 导出对象
export default ModalLoad;

4.使用方式

// 引入
import ModalLoading from "./components/index";
Vue.use(ModalLoading);

// 使用
Vue.showLoading();

5.效果

loading.gif

关于css animation中的animation-timing-function的取值,比如文中提及的steps()函数,可以参考MDN web docs

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

推荐阅读更多精彩内容