zoukankan      html  css  js  c++  java
  • uniapp实现骨架屏

    前言:用户在等待数据渲染的时候,有可能因为网络速度慢,手机硬件等问题,造成等待时间延长,使得用户体验不好。

    之前的做法是放个加载中的图标,而现在是直接根据页面原有元素绘制图形的方式,让用户有种页面就快渲染好的错觉。

    参考资料:

    https://ext.dcloud.net.cn/plugin?id=256

    备注:我是准备应用到项目中,从uniapp的插件市场下载了demo,结果出现一些小问题,在下载下来的demo做了些小修改

    加载过程效果图:如图,从图一到图二,最底部多出了一个动态加载的骨架,模拟同一页面多个数据请求(每个请求所需时间不同),

      我这边的处理是在每个请求的回调中,先赋值渲染的动态数据,再重新抓取需要绘制的动态元素(因为绘制的元素需要先有数据给它撑开),

      最后页面中的请求基本完成的时候,隐藏骨架屏,显示原先的页面

    问题:对demo有更好建议的可以提出来哈,相互学习一下

    代码如下:

    组件

      1 <template>
      2     <view v-if="show" :style="{ systemInfo.width + 'px', height: systemInfo.height + 'px', backgroundColor: bgcolor, position: 'absolute', left: 0, top: 0, zIndex: 9998, overflow: 'hidden'}">
      3         <view v-for="(item,rect_idx) in skeletonRectLists" :key="rect_idx + 'rect'" :class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']" 
      4         :style="{ item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view>
      5         <view v-for="(item,circle_idx) in skeletonCircleLists" :key="circle_idx + 'circle'" :class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''" 
      6         :style="{ item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214)', borderRadius: item.width + 'px', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}"></view>
      7         <view class="spinbox" v-if="loading == 'spin'">
      8             <view class="spin"></view>
      9         </view>
     10     </view>
     11 </template>
     12 
     13 <script>
     14     export default {
     15         name: "skeleton",
     16         props: {
     17             bgcolor: {
     18                 type: String,
     19                 value: '#FFF'
     20             },
     21             selector: {
     22                 type: String,
     23                 value: 'skeleton'
     24             },
     25             loading: {
     26                 type: String,
     27                 value: 'spin'
     28             },
     29             show: {
     30                 type: Boolean,
     31                 value: false
     32             },
     33             isNodes: {
     34                 type: Number,
     35                 value: false
     36             } //控制什么时候开始抓取元素节点,只要数值改变就重新抓取
     37         },
     38         data() {
     39             return {
     40                 loadingAni: ['spin', 'chiaroscuro'],
     41                 systemInfo: {},
     42                 skeletonRectLists: [],
     43                 skeletonCircleLists: []
     44             }
     45         },
     46         watch: {
     47             isNodes (val) {
     48                 this.readyAction();
     49             }
     50         },
     51         mounted() {
     52             this.attachedAction();
     53         },
     54         methods: {
     55             attachedAction: function(){
     56                 //默认的首屏宽高,防止内容闪现
     57                 const systemInfo = uni.getSystemInfoSync();
     58                 this.systemInfo = {
     59                      systemInfo.windowWidth,
     60                     height: systemInfo.windowHeight
     61                 };
     62                 this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin';
     63             },
     64             readyAction: function(){
     65                 console.log('子组件readyAction')
     66                 const that = this;
     67                 //绘制背景
     68                 uni.createSelectorQuery().selectAll(`.${this.selector}`).boundingClientRect().exec(function(res){
     69                     that.systemInfo.height = res[0][0].height + res[0][0].top;
     70                 });
     71                 
     72                 //绘制矩形
     73                 this.rectHandle();
     74 
     75                 //绘制圆形
     76                 this.radiusHandle();
     77             },
     78             rectHandle: function(){
     79                 const that = this;
     80 
     81                 //绘制不带样式的节点
     82                 uni.createSelectorQuery().selectAll(`.${this.selector}-rect`).boundingClientRect().exec(function(res){
     83                     that.skeletonRectLists = res[0];
     84                 });
     85 
     86             },
     87             radiusHandle(){
     88                 const that = this;
     89 
     90                 uni.createSelectorQuery().selectAll(`.${this.selector}-radius`).boundingClientRect().exec(function(res){
     91                     that.skeletonCircleLists = res[0];
     92                 });
     93             }
     94         }
     95     }
     96 </script>
     97 
     98 <style>
     99 .spinbox{
    100   position: fixed;
    101   display: flex;
    102   justify-content: center;
    103   align-items: center;
    104   height: 100%;
    105    100%;
    106   z-index: 9999
    107 }
    108 .spin {
    109   display: inline-block;
    110    64rpx;
    111   height: 64rpx;
    112 }
    113 .spin:after {
    114   content: " ";
    115   display: block;
    116    46rpx;
    117   height: 46rpx;
    118   margin: 1rpx;
    119   border-radius: 50%;
    120   border: 5rpx solid #409eff;
    121   border-color: #409eff transparent #409eff transparent;
    122   animation: spin 1.2s linear infinite;
    123 }
    124 @keyframes spin {
    125   0% {
    126     transform: rotate(0deg);
    127   }
    128   100% {
    129     transform: rotate(360deg);
    130   }
    131 }
    132 
    133 .chiaroscuro{
    134    100%;
    135   height: 100%;
    136   background: rgb(194, 207, 214);
    137   animation-duration: 2s;
    138   animation-name: blink;
    139   animation-iteration-count: infinite;
    140 }
    141 
    142 @keyframes blink {
    143   0% {
    144     opacity: .4;
    145   }
    146   50% {
    147     opacity: 1;
    148   }
    149   100% {
    150     opacity: .4;
    151   }
    152 }
    153 
    154 @keyframes flush {
    155   0% {
    156     left: -100%;
    157   }
    158   50% {
    159     left: 0;
    160   }
    161   100% {
    162     left: 100%;
    163   }
    164 }
    165 .shine {
    166   animation: flush 2s linear infinite;
    167   position: absolute;
    168   top: 0;
    169   bottom: 0;
    170    100%;
    171   background: linear-gradient(to left,
    172   rgba(255, 255, 255, 0) 0%,
    173   rgba(255, 255, 255, .85) 50%,
    174   rgba(255, 255, 255, 0) 100%
    175   )
    176 }
    177 </style>
    View Code

    页面demo

      1 <template>
      2     <view class="controller">
      3         <view class="container skeleton" :style="{visibility: showSkeleton ? 'hidden' : 'visible'}">
      4             <view class="userinfo">
      5                 <block>
      6                     <!--skeleton-radius 绘制圆形-->
      7                     <image class="userinfo-avatar skeleton-radius" :src="userInfo.avatarUrl" mode="cover"></image>
      8                      <!--skeleton-rect 绘制矩形-->
      9                     <text class="userinfo-nickname skeleton-rect">{{userInfo.nickName}}</text>
     10                 </block>
     11             </view>
     12             <view style="margin: 20px 0">
     13                 <view v-for="(item,index) in lists" :key="index" class="lists">
     14                     <text class="skeleton-rect">{{item}}</text>
     15                 </view>
     16             </view>
     17             <view class="usermotto">
     18                 <text class="user-motto skeleton-rect">{{motto}}</text>
     19             </view>
     20         </view>
     21         <!--引用组件-->
     22         <skeleton :show="showSkeleton" :isNodes="isNodes" ref="skeleton" loading="chiaroscuro" selector="skeleton" bgcolor="#FFF"></skeleton>
     23     </view>
     24 </template>
     25 
     26 <script>
     27     //引入骨架屏组件(以我本地地址为例,具体地址由自身引用位置决定)
     28     import skeleton from "@/components/quick-skeleton/quick-skeleton.vue";
     29     export default {
     30         data() {
     31             return {
     32                 motto: '',
     33                 userInfo: {
     34                     avatarUrl: 'https://wx.qlogo.cn/mmopen/vi_32/s4RzXCAQsVNliaJXtHBvdpAkeRwnK7Jhiaf9mzuVqEhZza3zSYM7tJ1xZCQE9SCoOR8qjVEjDKltw1SQnxyicWq6A/132',
     35                     nickName: 'jayzou'
     36                 },
     37                 // lists: [
     38                 //     '第1行数据',
     39                 //     '第2行数据',
     40                 //     '第3行数据',
     41                 //     '第4行数据',
     42                 //     '第5行数据',
     43                 //     '第6行数据'
     44                 // ],
     45                 lists: [], //如果没有默认数据
     46                 showSkeleton: true,  //骨架屏显示隐藏
     47                 isNodes: 0 //控制什么时候开始抓取元素节点,只要数值改变就重新抓取
     48             };
     49         },
     50         components: {
     51             skeleton
     52         },
     53         onLoad: function () {
     54             const that = this;
     55             
     56             //问题:骨架屏出现的时间段,部分已经渲染完毕,但还是得等骨架屏隐藏才一起出现
     57             
     58             setTimeout(() => {
     59                 this.lists = [
     60                     '第1行数据',
     61                     '第2行数据',
     62                     '第3行数据',
     63                     '第4行数据',
     64                     '第5行数据',
     65                     '第6行数据'
     66                 ]
     67                 that.isNodes ++;
     68             }, 182);
     69             
     70             setTimeout(() => {
     71                 that.motto = 'Hello World'
     72                 that.isNodes ++;
     73             }, 500);
     74             
     75             setTimeout(() => {
     76                 that.showSkeleton = false;
     77             }, 2000);
     78         },
     79         /**
     80          *  页面载入完成后调用子组件的方法生成预加载效果
     81          */
     82         onReady:function(){
     83             
     84         }
     85     }
     86 </script>
     87 
     88 <style>
     89 .container {
     90     padding: 20upx 60upx;
     91 }
     92 /**index.wxss**/
     93 .userinfo {
     94   display: flex;
     95   flex-direction: column;
     96   align-items: center;
     97 }
     98 .userinfo-avatar {
     99    128rpx;
    100   height: 128rpx;
    101   margin: 20rpx;
    102   border-radius: 50%;
    103 }
    104 .userinfo-nickname {
    105   color: #aaa;
    106 }
    107 .usermotto {
    108   margin-top: 200px;
    109 }
    110 .lists{
    111   margin: 10px 0;
    112 }
    113 .list{
    114   margin-right: 10px;
    115 }
    116 </style>
    View Code
  • 相关阅读:
    华为设备SNMP配置
    Linux CP直接覆盖快速操作
    CentOS7快速安装PHP7.0指南
    Linux SSH免密码登录配置
    this的指向
    分页的模块layui
    multer中间件
    ajax实现图片上传
    MVC模式、加密、jsonwebtoken
    mongoose与express
  • 原文地址:https://www.cnblogs.com/lightmusic/p/12761239.html
Copyright © 2011-2022 走看看