uniapp实现⾻架屏
前⾔:⽤户在等待数据渲染的时候,有可能因为⽹络速度慢,⼿机硬件等问题,造成等待时间延长,使得⽤户体验不好。
之前的做法是放个加载中的图标,⽽现在是直接根据页⾯原有元素绘制图形的⽅式,让⽤户有种页⾯就快渲染好的错觉。
参考资料:
备注:我是准备应⽤到项⽬中,从uniapp的插件市场下载了demo,结果出现⼀些⼩问题,在下载下来的demo做了些⼩修改
加载过程效果图:如图,从图⼀到图⼆,最底部多出了⼀个动态加载的⾻架,模拟同⼀页⾯多个数据请求(每个请求所需时间不同),
  我这边的处理是在每个请求的回调中,先赋值渲染的动态数据,再重新抓取需要绘制的动态元素(因为绘制的元素需要先有数据给它撑开),  最后页⾯中的请求基本完成的时候,隐藏⾻架屏,显⽰原先的页⾯
问题:对demo有更好建议的可以提出来哈,相互学习⼀下
代码如下:
组件
1 <template>
2    <view v-if="show" :>
3        <view v-for="(item,rect_idx) in skeletonRectLists" :key="rect_idx + 'rect'" :class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']"
4        :></view>
5        <view v-for="(item,circle_idx) in skeletonCircleLists" :key="circle_idx + 'circle'" :class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''"
6        :></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,
htmlborder23                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() {
39return {
40                loadingAni: ['spin', 'chiaroscuro'],
41                systemInfo: {},
42                skeletonRectLists: [],
43                skeletonCircleLists: []
44            }
45        },
46        watch: {
47            isNodes (val) {
49            }
50        },
51        mounted() {
52this.attachedAction();
53        },
54        methods: {
55            attachedAction: function(){
56//默认的⾸屏宽⾼,防⽌内容闪现
57                const systemInfo = SystemInfoSync();
58this.systemInfo = {
59                    width: systemInfo.windowWidth,
60                    height: systemInfo.windowHeight
61                };
62this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin';
63            },
64            readyAction: function(){
65                console.log('⼦组件readyAction')
66                const that = this;
67//绘制背景
68                ateSelectorQuery().selectAll(`.${this.selector}`).boundingClientRect().exec(function(res){
69                    that.systemInfo.height = res[0][0].height + res[0][0].top;
70                });
71
72//绘制矩形
74
75//绘制圆形
76this.radiusHandle();
77            },
78            rectHandle: function(){
79                const that = this;
80
81//绘制不带样式的节点
82                ateSelectorQuery().selectAll(`.${this.selector}-rect`).boundingClientRect().exec(function(res){
83                    that.skeletonRectLists = res[0];
84                });
85
86            },
87            radiusHandle(){
88                const that = this;
89
90                ateSelectorQuery().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  width: 100%;
106  z-index: 9999
107 }
108 .spin {
109  display: inline-block;
110  width: 64rpx;
111  height: 64rpx;
112 }
113 .spin:after {
114  content: " ";
115  display: block;
116  width: 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  width: 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  width: 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" :>
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 >
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() {
31return {
32                motto: '',
33                userInfo: {
34                    avatarUrl: 'wx.qlogo/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(() => {
59this.lists = [
60                    '第1⾏数据',
61                    '第2⾏数据',
62                    '第3⾏数据',
63                    '第4⾏数据',
64                    '第5⾏数据',
65                    '第6⾏数据'
66                ]
67                that.isNodes ++;
68            }, 182);
69
70            setTimeout(() => {
71                = '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  width: 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

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。