背景
小程序首页进入时,需要等待后端数据返回进行渲染,在等待过程中页面可能会是空屏状态,为减少用户等待的感知,加入骨架屏进行体验优化。
原理
通过类名标记需要“骨架”化的节点,再通过skeleton
组件获取这些节点的位置大小信息,渲染出位置大小与真实节点一致的骨架节点,利用定位与层级对原页面覆盖。在请求过程中展示skeleton
组件,请求完成后则隐藏,从而达到预期效果。
实现
首先我们在页面中引入骨架屏的skeleton
组件
因为我们最终要获取各骨架节点的信息,所以需要对他们提前使用类名标记
1. `skeleton` 页面根节点
2. `skeleton-rect` 需要绘成方形骨架的节点
3. `skeleton-arc ` 需要绘成圆形骨架的节点
// index.wxml
<skeleton isShow="{{ loading }}" bgColor="#ccc"></skeleton>
<view class="skeleton container">
<image catchtap="onClick" class="avatar skeleton-arc" src="..."></image>
<user-name></user-name>
<view class="block skeleton-rect"></view>
<view class="block skeleton-rect"></view>
<view class="block skeleton-rect"></view>
<view class="block skeleton-rect"></view>
<view class="block skeleton-rect"></view>
<view class="block skeleton-rect"></view>
</view>
骨架屏skeleton
组件的实现
-
将组件设为绝对定位(
fixed
),高层级(z-index
),宽高设为父级的100% -
页面节点挂载完成(
ready()
)时,用节点查询方法(wx.createSelectorQuery().selectAll()
),找到所有相关类名的元素(skeleton-rect
,skeleton-arc
) -
找到所有元素后,根据方圆分别用两个数组存放位置大小信息,最终使用
wx:for
完成渲染
// skeleton.js
Component({
properties: {
isShow: { // 是否展示
type: Boolean,
value: true
},
bgColor: { // 骨架屏背景
type: String,
value: '#fff'
},
selects: { // 页面根元素类名
type: String,
value: 'skeleton'
}
},
data: {
skeletonRect: [], // 方形列表
skeletonArc: [] // 圆形列表
},
ready() {
this.fillRect()
this.fillCircle()
},
methods: {
// 绘制方形
fillRect() {
wx.createSelectorQuery()
.selectAll(`.${this.data.selects} >>> .${this.data.selects}-rect`)
.boundingClientRect((rect) => {
this.setData({
skeletonRect: rect
})
})
.exec()
},
// 绘制圆形
fillCircle() {
wx.createSelectorQuery()
.selectAll(`.${this.data.selects} >>> .${this.data.selects}-arc`)
.boundingClientRect((rect) => {
this.setData({
skeletonArc: rect
})
})
.exec()
},
}
})
// skeleton.wxml
<view style="background: {{bgColor}};" class="skeleton-wrap" wx:if="{{isShow}}">
<!--画圆-->
<view class="skeleton-item skeleton-ani" wx:for="{{skeletonRect}}" wx:key="id"
style="width: {{item.width}}px; height: {{item.height}}px; top: {{item.top}}px; left: {{item.left}}px;">
</view>
<!--画方-->
<view class="skeleton-item skeleton-ani" wx:for="{{skeletonArc}}"
wx:key="id" style="width: {{item.width}}px; height: {{item.height}}px; top: {{item.top}}px; left: {{item.left}}px; border-radius: {{item.width}}px;">
</view>
</view>
// skeleton.wxss
.skeleton-wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9998;
overflow: hidden;
}
.skeleton-item{
position: absolute;
background-color: #eee;
}
/*动画*/
.skeleton-ani {
background: linear-gradient(
110deg,
transparent 40%,
rgba(255, 255, 255, .5) 50%,
transparent 60%) #eee;
background-size: 200%;
background-position-x: 180%;
animation: ani 1.5s linear infinite;
}
@keyframes ani {
to {
background-position-x: -20%;
}
}
获取页面相关节点, 使用跨组件的后代选择器>>>
,可以拿到其它自定义内的skeleton-rect/acr
节点