vant加载组件不够用,那就手写一个loading组件


theme: fancy
highlight: arduino-light


本文正在参加「金石计划 . 瓜分6万现金大奖」

前言

  • 常网IT戳我呀!
  • 常网IT源码上线啦!
  • 如果是海迷,RED红发剧场版有需要可关注我主页公众号回“海贼王red”领取。
  • 已有专栏Vue吊打面试官,各位看官感兴趣可移步🚶。
  • 最近在开发过程中,发现vant官网提供的组件不足以满足需求,所以手写一个Loading组件。

每个组件都有自己的出场机会。

1.jpg

一、存在问题

在实战开发中,vant作为移动端UI的一把手,想必我们都很熟悉。

1.1 需求

最近开发过程中,想实现接口请求中,展示全屏loading,接口请求回来,关闭全屏loading。

  • 覆盖全屏的loading加载

  • 通过JavaScript控制显示与否

1.2 Loading 加载

于是,我们翻了一下官方组件,发现vant提供了Loading加载组件。

组件似乎只适用于上拉下拉刷新,展示的加载状态❌。

而我们想要的是全屏的Loading,所以不太适用。

2.gif

1.3 Toast 轻提示

官方还提供Toast组件。

这个效果正是我们所想要的,通过JavaScript让其创建显示。

// 自定义加载图标 
Toast.loading({
    message: '加载中...', 
    forbidClick: true, 
    loadingType: 'spinner', 
});

很可惜的一点是:官方并没有提供手动隐藏的API,只提供xxx毫秒后自动关闭。

也有可能有,但我没找到。

3.gif

既然官方提供的组件不合我意,那就手写一个Loading加载组件吧!

二、Loading组件诞生

html结构如下:

<template>
  <div>
    <div class="load">
      <!-- 中间的图案动效加载 -->
      <div class="sk-chase"></div>
      <!-- 文字加载 -->
      <span>加载中...</span>
    </div>
    <!-- 全屏遮罩层 -->
    <div class="full-screen"></div>
  </div>
</template>

我们的class:full-screen作为全屏的遮罩层,定位应是fixed固定定位,防止页面滑动上下滚动而下层无法遮罩。

class:load作为中间的动效加载,定位也应是fixed固定定位,屏幕居中,我们设置 left: 50% 和 top: 50% 现将子元素左上角移到父元素中心位置,然后再通过 translate 来调整子元素的中心点到父元素的中心。该方法可以不定宽高

class:sk-chase作为中间的图案动效加载,既然是动效,那离不开我们的animation,设置为infinite无限。

4.gif

加载是转圈的,那么可利用rotate(360deg)

而且我们的加载过程中大小会时大时小,可利用scale(0.4) -> scale(1)

三、代码如下

Loading.vue

<!--
 * @Description: Load加载 -- 组件
-->
<template>
  <div>
    <div class="load">
      <!-- 中间的图案动效加载 -->
      <div class="sk-chase">
        <div class="sk-chase-dot" v-for="(item, key) in 6" :key="key"></div>
      </div>
      <!-- 文字加载 -->
      <span>{{ title }}</span>
    </div>
    <!-- 全屏遮罩层 -->
    <div class="full-screen"></div>
  </div>
</template>

<script>
export default {
  name: "loading",
  props: {
    title: {
      type: String,
      default: "加载中...",
    },
  },
  data() {
    return {};
  },
};
</script>

<style scoped="scoped" lang="scss">
.full-screen {
  position: fixed;
  overflow: hidden;
  left: 0;
  top: 0;
  background: rgba(255, 255, 255, 0.3);
  width: 100%;
  height: 100%;
  z-index: 1;
}
.load {
  color: #dfdfdf;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 120px;
  height: 120px;
  border-radius: 8px;
  background: rgba(74, 74, 74, 0.9);
  z-index: 2;
  span {
    position: absolute;
    bottom: 15%;
    left: 25%;
  }
}
.sk-chase {
  width: 40px;
  height: 40px;
  position: absolute;
  top: 20px;
  left: 35%;
  animation: sk-chase 2.5s infinite linear both;
}

.sk-chase-dot {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
  animation: sk-chase-dot 2s infinite ease-in-out both;
}

.sk-chase-dot:before {
  content: "";
  display: block;
  width: 25%;
  height: 25%;
  background-color: #dfdfdf;
  border-radius: 100%;
  animation: sk-chase-dot-before 2s infinite ease-in-out both;
}

.sk-chase-dot:nth-child(1) {
  animation-delay: -1.1s;
}
.sk-chase-dot:nth-child(2) {
  animation-delay: -1s;
}
.sk-chase-dot:nth-child(3) {
  animation-delay: -0.9s;
}
.sk-chase-dot:nth-child(4) {
  animation-delay: -0.8s;
}
.sk-chase-dot:nth-child(5) {
  animation-delay: -0.7s;
}
.sk-chase-dot:nth-child(6) {
  animation-delay: -0.6s;
}
.sk-chase-dot:nth-child(1):before {
  animation-delay: -1.1s;
}
.sk-chase-dot:nth-child(2):before {
  animation-delay: -1s;
}
.sk-chase-dot:nth-child(3):before {
  animation-delay: -0.9s;
}
.sk-chase-dot:nth-child(4):before {
  animation-delay: -0.8s;
}
.sk-chase-dot:nth-child(5):before {
  animation-delay: -0.7s;
}
.sk-chase-dot:nth-child(6):before {
  animation-delay: -0.6s;
}

@keyframes sk-chase {
  100% {
    transform: rotate(360deg);
  }
}

@keyframes sk-chase-dot {
  80%,
  100% {
    transform: rotate(360deg);
  }
}

@keyframes sk-chase-dot-before {
  50% {
    transform: scale(0.4);
  }
  100%,
  0% {
    transform: scale(1);
  }
}
</style>

于是我们在外层引入Loading.vue组件。

<Loading v-show="fullscreenLoading" title="提交中..." />

async getData(){
    this.fullscreenLoading = true
    const res = await get()
    this.fullscreenLoading = false
}

看下效果。

5.png

一个简简单单的Loading组件便大功告成。

Loading组件已放在Github,点我下载👈

四、水平垂直居中

我们在上面有说到加载效果要在屏幕的居中显示。

那顺便来道经典的面试题,如何实现水平垂直居中?

4.1 绝对定位(left、top50%、translate)

利用绝对定位,设置 left: 50% 和 top: 50% 现将子元素左上角移到父元素中心位置,然后再通过 translate 来调整子元素的中心点到父元素的中心。该方法可以不定宽高

注意这里启动了3D硬件加速哦 会增加耗电量的。

.father {
  position: relative;
}
.son {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%); 
  /*
    自身宽度一半,等同于margin-left: -50px; 等同于margin-top: -50px;
    想想如不设置transform偏移的话,son元素整体会向右下
    因为没有减掉son元素自身的宽高呀
  */
}

父相re子绝ab。

4.2 绝对定位(全部方向0、margin: auto)

利用绝对定位,子元素所有方向都为 0 ,将 margin 设置为 auto ,由于宽高固定,对应方向实现平分,该方法必须盒子有宽高

.father {
  position: relative;
}
.son {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0px;
  margin: auto;
  height: 100px;
  width: 100px;
}

4.3 绝对定位(l、t50%、m-t-l宽高一半)

利用绝对定位,设置 left: 50% 和 top: 50% 现将子元素左上角移到父元素中心位置,然后再通过 margin-left 和 margin-top 以子元素自己的一半宽高进行负值赋值。该方法必须定宽高

感觉看了圣杯模式的视频,现在看margin负值觉得很简单、好理解了😁。

关于圣杯模式、两栏布局后期出篇文章。

.father {
  position: relative;
}
.son {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 200px;
  height: 200px;
  margin-left: -100px;
  margin-top: -100px;
}

4.4 flex横扫千军

利用 flex ,最经典最方便的一种了,不用解释,定不定宽高无所谓的。

其实还有很多方法,比如 display: grid 或 display: table-cell 来做,有兴趣点击下面这篇文章可以了解下:你能实现多少种水平垂直居中的布局(定宽高和不定宽高)

.father {
  display: flex;
  justify-content: center;
  align-items: center;
}

4.5 总结

4.5.1 水平居中

  • 对于水平居中,我们应该先考虑,哪些元素有自带的居中效果,最先想到的应该就是 text-align:center 了,但是这个只对行内内容有效,所以我们要使用 text-align:center 就必须将子元素设置为 display: inline; 或者 display: inline-block; ;
  • 其次就是考虑能不能用margin: 0 auto;因为这都是一两句代码能搞定的事,实在不行就是用绝对定位去实现了。
  • 移动端能用flex就用flex,简单方便,灵活并且功能强大,无愧为网页布局的一大利器!

4.5.2 垂直居中

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容