用于小程序中的骨架屏
基于uni-app的一个骨架屏插件。
在使用的时候可以直接在components中引入组件quick-skeleton.vue。组件代码如下:
1 <template>
2 <view
3 v-show="show"
4 :style="{
5 width: systemInfo.width + 'px',
6 height: systemInfo.height + 'px',
7 backgroundColor: bgcolor,
8 position: 'absolute',
9 left: 0,
10 top: 0,
11 zIndex: 9998,
12 overflow: 'hidden'
13 }"
14 >
15 <view
16 v-for="(item, rect_idx) in skeletonRectLists"
17 :key="rect_idx + 'rect'"
18 :class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']"
19 :style="{
20 width: item.width + 'px',
21 height: item.height + 'px',
22 backgroundColor: '#f4f4f',
23 position: 'absolute',
24 left: item.left + 'px',
25 top: item.top + 'px'
26 }"
27 ></view>
28 <view
29 v-for="(item, circle_idx) in skeletonCircleLists"
30 :key="circle_idx + 'circle'"
31 :class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''"
32 :style="{
33 width: item.width + 'px',
34 height: item.height + 'px',
35 backgroundColor: '#f4f4f',
36 borderRadius: item.width + 'px',
37 position: 'absolute',
38 left: item.left + 'px',
39 top: item.top + 'px'
40 }"
41 ></view>
42
43 <view class="spinbox" v-if="loading == 'spin'"><view class="spin"></view></view>
44 </view>
45 </template>
46
47 <script>
48 export default {
49 name: 'skeleton',
50 props: {
51 bgcolor: {
52 type: String,
53 value: '#FFF'
54 },
55 selector: {
56 type: String,
57 value: 'skeleton'
58 },
59 loading: {
60 type: String,
61 value: 'spin'
62 },
63 show: {
64 type: Boolean,
65 value: false
66 }
67 },
68 data() {
69 return {
70 loadingAni: ['spin', 'chiaroscuro'],
71 systemInfo: {},
72 skeletonRectLists: [],
73 skeletonCircleLists: []
74 };
75 },
76 watch: {
77 show() {
78 this.attachedAction();
79 this.readyAction();
80 }
81 },
82 methods: {
83 attachedAction: function() {
84 //默认的首屏宽高,防止内容闪现
85 const systemInfo = uni.getSystemInfoSync();
86 this.systemInfo = {
87 width: 750,
88 height: 1440
89 };
90 this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin';
91 },
92 readyAction: function() {
93 const that = this;
94 //绘制背景
95 uni.createSelectorQuery()
96 .selectAll(`.${this.selector}`)
97 .boundingClientRect()
98 .exec(function(res) {
99 that.systemInfo.height = res[0][0].height + res[0][0].top;
100 });
101
102 //绘制矩形
103 this.rectHandle();
104
105 //绘制圆形
106 this.radiusHandle();
107 },
108 rectHandle: function() {
109 const that = this;
110
111 //绘制不带样式的节点
112 uni.createSelectorQuery()
113 .selectAll(`.${this.selector}-rect`)
114 .boundingClientRect()
115 .exec(function(res) {
116 that.skeletonRectLists = res[0];
117 });
118 },
119 radiusHandle() {
120 const that = this;
121
122 uni.createSelectorQuery()
123 .selectAll(`.${this.selector}-radius`)
124 .boundingClientRect()
125 .exec(function(res) {
126 that.skeletonCircleLists = res[0];
127 });
128 }
129 }
130 };
131 </script>
132
133 <style>
134 .spinbox {
135 position: fixed;
136 display: flex;
137 justify-content: center;
138 align-items: center;
139 height: 100%;
140 width: 100%;
141 z-index: 9999;
142 }
143 .spin {
144 display: inline-block;
145 width: 64rpx;
146 height: 64rpx;
147 }
148 .spin:after {
149 content: ' ';
150 display: block;
151 width: 46rpx;
152 height: 46rpx;
153 margin: 1rpx;
154 border-radius: 50%;
155 border: 5rpx solid #409eff;
156 border-color: #409eff transparent #409eff transparent;
157 animation: spin 1.2s linear infinite;
158 }
159 @keyframes spin {
160 0% {
161 transform: rotate(0deg);
162 }
163 100% {
164 transform: rotate(360deg);
165 }
166 }
167
168 .chiaroscuro {
169 width: 100%;
170 height: 100%;
171 background: #f4f4f4;
172 animation-duration: 2s;
173 animation-name: blink;
174 animation-iteration-count: infinite;
175 }
176
177 @keyframes blink {
178 0% {
179 opacity: 0.5;
180 }
181 50% {
182 opacity: 1;
183 }
184 100% {
185 opacity: 0.5;
186 }
187 }
188
189 @keyframes flush {
190 0% {
191 left: -100%;
192 }
193 50% {
194 left: 0;
195 }
196 100% {
197 left: 100%;
198 }
199 }
200 .shine {
201 animation: flush 2s linear infinite;
202 position: absolute;
203 top: 0;
204 bottom: 0;
205 width: 100%;
206 background: linear-gradient(to left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.85) 50%, rgba(255, 255, 255, 0) 100%);
207 }
208 </style>
引入代码后,在需要加载骨架屏的页面中,为整个页面的盒子加一类名skeleton,并加一个兄弟节点quick-skeleton引入组件:
showSkeleton控制骨架屏是否显示;
骨架屏显示时catchtouchmove和fixed控制页面是否可以滑动,这里用fixed让页面不可滑动;
bgcolor为骨架屏的背景颜色。其余属性不需改动,可以直接使用。
在onReady()中控制骨架屏的加载,
若是即时加载的骨架屏,可以将_this.showSkeleton = false;放在接口调用成功之后,这样就实现了加载完后骨架屏自动消失的效果,这里暂用定时器来实现骨架屏的隐藏。
1 <view>
2 <quick-skeleton
3 :show="showSkeleton"
4 ref="skeleton"
5 catchtouchmove="true"
6 fixed="true"
7 loading="chiaroscuro"
8 selector="skeleton"
9 bgcolor="#FFF"
10 style="overflow: hidden;"
11 ></quick-skeleton>
12 <div class="page-content skeleton"></div>
13 </view>
14 <script>
15 import quickSkeleton from '../../components/quick-skeleton.vue';
16 export default {
17 data() {
18 return {
19 showSkeleton: true,
20 };
21 },
22 components: {
23 quickSkeleton
24 },
25 onReady() {
26 let _this = this;
27 _this.$refs.skeleton.attachedAction();
28 _this.$refs.skeleton.readyAction();
29 setTimeout(function() {
30 _this.showSkeleton = false;
31 }, 3000);
32 },
33 }
34 </script>
然后只要在页面中为想要生成骨架屏的元素加类名即可:
skeleton-radius表示圆形,skeleton-rect表示矩形
注:如果是利用v-for动态生成的结构,不能自动生成骨架屏,所以这里写出结构模板,给模板元素固定的宽高和背景颜色(与骨架屏相同),再用showSkeleton控制页面加载完成后隐藏结构模板。
1 <div v-show="showSkeleton"> 2 <navigator hover-class="none" class=""> 3 <div class=""> 4 <div class="skeleton-radius" style="background: #f5f6f7;"><img class="" mode="" /></div> 5 <div class=""> 6 <span class="skeleton-rect" style="background: #f5f6f7;height: 26px;"></span> 7 <div class="skeleton-rect" style="background: #f5f6f7;height: 15px;"></div> 8 </div> 9 </div> 10 <ul class="skeleton-rect" style="background: #f5f6f7;"></ul> 11 <ul class="skeleton-rect" style="background: #f5f6f7;"> 12 <li class=""><img class="" mode="" /></li> 13 <li class=""><img class="" mode="" /></li> 14 <li class=""><img class="" mode="" /></li> 15 </ul> 16 <div class="skeleton-rect" style="background: #f5f6f7;height: 20px;"></div> 17 </navigator> 18 </div>
此骨架屏为颜色渐浅的闪烁效果,如果想要修改的话只需在组件中修改css。
效果如下:


浙公网安备 33010602011771号