前言
骨架屏是一种能良好提高用户体验的常见手段,不至于刚进应用就看到页面排版混乱的恶心体验,不过之前大多都是在H5的应用中,本章就聊聊在微信小程序中如何使用骨架屏,以及探讨下骨架屏的实现原理。
在了解下面的内容前,有件很重要的事情:
- 小程序不存在预渲染的概念!!!
- 小程序不存在预渲染的概念!!!
- 小程序不存在预渲染的概念!!!
重要的事情说三遍,所以我们不存在说先提前渲染好页面返回给用户的情况。
但是呢!但是呢!我们还是可以通过骨架屏组件的使用,来很好的等待一些异步数据更新渲染的,下面就来开始聊聊吧。
使用过程
从上面的效果图我们能看到大致的效果了,我们先聊聊大致的使用过程,分三步走。
- 引入组件
我们把骨架屏做成一个公共组件,把它引入到想要加的页面中。
// index.json
{
"usingComponents": {
"skeleton": "./../../components/skeleton/skeleton"
}
}
- 添加组件结构,给页面根节点 class 添加
skeleton类名,给需要画成方形的元素添加skeleton-rect类名,给需要画成圆形的元素添加skeleton-arc类名。
// index.wxml
<!-- 骨架屏 -->
<skeleton isShow="{{ loading }}"></skeleton>
<view class="skeleton container">
<image class="avatar skeleton-arc" src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/1/6/16f786fbd44167e9~tplv-t2oaga2asx-image.image"></image>
<view class="username 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 class="block skeleton-rect"></view>
</view>
- 特定时机销毁组件
// index.js
const app = getApp()
Page({
data: {
loading: true
},
onLoad() {
// 模拟异步请求5秒后销毁组件
setTimeout(() => {
this.setData({
loading: false
})
}, 5000)
}
})
在小程序中使用骨架屏还是比较简单的 easy~ 是吧,主要就是注意什么时机来把控骨架屏的显示与隐藏就完事。
实现原理
接下来我们就来分析下实现这个骨架屏组件的原理吧,(先打个针)其实也是非常简单的 easy~ 啦。
- 首先把组件设置为绝对定位(
fixed),设定一个比较高的层级(z-index),大小为整个屏幕的大小也就是100%。 - 在页面节点挂载完成(
ready())的时候,用节点查询方法(wx.createSelectorQuery().selectAll()),找到所有相关类名的元素,也就是我们前面说过的添加在skeleton类名下的有skeleton-rect类名与skeleton-arc类名的元素了。 - 找到所有元素后,将它们的位置与大小信息储存起来,我们把元素划分为方形与圆形两个两个数组储存,最后用
wx:for给渲染出来就大功告成了。
大致就是怎么个过程了,根据这个分析相信你也差不多能写个大概了,下面就来看看小编的实现过程吧。
过程一
先把结构与样式怼您出来看看吧,没啥难度^-^。
// skeleton.wxml
<view style="background: {{bgColor}};" class="skeleton-wrap" catch:touchmove="_none" 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>
样式也没多少,主要可能需要注意的就是添加了一个小动画效果,它利用CSS3的背景渐变属性linear-gradient与动画animation来实现,对于animation就不多说了,比较常见了,前端多多少少是知道了解的,不知道的拉出去打吧-.-。对于linear-gradient可能就见的比较少了,不懂的点它点它。点我
// 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%;
}
}
过程二
我们骨架屏已经是一个fixed布局且层级较高的组件,基本是完整遮挡住了页面,下面开始映射页面元素绘制出对应的方形与原型元素,我将所要绘制的元素都统一设置成absolute,再通过wx.createSelectorQuery().selectAll()API拿到相关元素的width、height、top与left,用来绘制元素就行啦。
// 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 () {
// 获取页面相关节点, 使用跨组件的后代选择器(>>>), 注意if和hidden的拿不到
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-rect类名与skeleton-arc类名来进入自己另外定义的组件里面,但只要它在页面根节点上有skeleton类名的下面,就一样能获取到该元素的信息。
附上wx.createSelectorQuery().selectAll()API获取的属性:
源码
以上就是本文的全部内容,是不是非常简单,没有骗你吧?即引即用,是不是也很实用呢?以上代码不要告诉我你看不懂(拉出去打...)。
最后送上源码 传送门,拜拜,下期见。