背景
所在项目在做陌陌小程序的相关产品,目前项目还处于技术核高基(踩坑)阶段。由于之前没有接触过骨架屏,所以非常心动,遂主动负责了这部份。本文记录总结了对于骨架屏实现的几种方案,供你们参考
预研方案一、 引入静态文件
引入静态文件应当是最容易想到的骨架屏解决方案。
引入静态文件这点挺好理解,其实就是为每位须要在加载的时侯显示骨架屏的页面都编撰一个骨架屏文件,并在相应的页面中引入编撰好的骨架屏页面。比如我有一个页面pages/牛牛/牛牛.wxml,那么我须要为这个页面编撰一个单独的wxml文件,当页面处于加载状态时显示这个骨架屏页面pages/牛牛/牛牛.skeleton.wxml。
如果骨架屏页面较多,那么可以思索一下,对于页面中骨架屏展示的内容基本都是右图的方式,当然内容和结构可能会多种多样。
#6:e:d:3:8:5:d:a:a:9:3:c:b:7:3:d:d:e:3:d:5:6:e:a:3:8:f:0:6:0:5:7#
但是细细想来,骨架屏中的内容基本都是由几种特定的规则图形来诠释,如:圆形、矩形、圆角方形等。所以,我们可以像普通的组件化开发一样,将一些通用的骨架屏内容抽成愈发可用的组件。
当然陌陌开发者工具也提供了生成骨架屏的功能:
#1:5:e:8:7:4:8:4:8:9:d:3:b:6:b:3:e:e:f:0:e:4:5:9:4:9:3:c:3:5:f:2#
当我们点击后,开发者工具会在dist中的当前页面目录下生成两个文件:page_name.skeleton.wxml和page_name.skeleton.wxss,剩下的形式就和我们引入静态文件的形式是一样的!下图为引入后的疗效:
#1:e:b:a:8:5:3:d:8:6:5:6:3:f:3:c:a:c:8:b:f:d:4:8:8:8:b:2:6:7:2:2#
可以看出手动生成的骨架屏的疗效还是不太能满足我们,最前面我们自定义的navigation-bar也弄成了骨架屏,这或许不是我们想要的,当然我们可以自动更改手动生成的page_name.skeleton.wxss文件。说到底陌陌生成的骨架屏与我们自己实现的方法一样,可以看一下生成的牛牛.skeleton.wxml文件:
#3:2:2:c:e:7:e:9:9:a:e:0:b:b:9:3:c:f:c:4:0:7:5:1:7:a:2:a:a:3:2:b#
生成的文件中,将我们的页面wxml中的内容添加相应的class,并在page_name.skeleton.wxss加入相应款式。
同时,每个class都还有相应的编码:
#f:e:4:e:6:b:f:8:c:d:4:0:a:e:5:0:3:3:6:3:f:e:d:e:6:a:c:c:6:c:4:1#
这也就意味着,当我们再度使用生成骨架屏的功能然后,我们之前的更改也会急剧删除!!!这种方法也很难用版本控制来管理生成的骨架屏文件。开发过小程序的小伙伴应当晓得,小程序会有包容积的限制,从我里面描述的可以看出陌陌生成骨架屏的形式生成的文件会有很多重复且冗余的代码,这无疑也降低了小程序包的容积。
同时,当设计的界面发生变化时,对于引入静态文件的形式也是非常不友好的,设计界面的改变造成相应的骨架屏实现也要急剧更改!
二、动态估算
动态估算是指当用户步入界面的时侯,我们使用陌陌提供的一些API的去获取当前页面一些指定区块的信息(如座标、尺寸等),然后在相应的座标处勾画与指定区块相同大小的红色区域来实现骨架屏疗效。
实现上去非常简单,首先在我们须要显示骨架屏的页面引入骨架屏组件,同时为须要展示位骨架屏的组件或则节点添加指定class,此处特定class为skeleton-rect,含义为“矩形骨架屏”:
#3:f:2:2:e:2:d:9:5:5:5:a:e:5:8:6:e:4:c:3:e:f:9:5:a:9:6:2:f:8:f:3#
下面,我们开始着手实现骨架屏组件:
#3:f:7:b:f:b:8:3:5:d:0:a:9:5:e:0:e:a:5:4:4:5:8:d:4:7:d:6:d:1:e:1#
#6:0:5:1:6:d:2:c:1:9:5:e:f:4:7:5:7:7:b:3:8:8:8:d:5:b:0:2:2:5:c:1#
wxml文件就不做过多介绍,在ts文件中,当组件setup(生命周期参照Vue3 Composition API)时我们使用wx.createSelectorQuery获取到所有具有skeleton-rect class 的节点,然后通过boundingClientRect获取到每位节点的绝对位置以及规格,然后组合成款式字符串存储进skeletonRectsStyles中供我们在wxml中使用。效果如图:
#e:b:1:8:4:5:c:c:4:4:8:4:5:6:5:1:9:e:f:d:d:d:8:a:a:8:7:e:4:d:1:e#
当然可以看出一些问题,在公告以及内容组件中我们会使用到margin以及padding等方法来调整款式,我们获取到的规格是包含了margin等信息的,所以动态估算生成的骨架屏显示下来会有些问题,如比加载完毕后显示的疗效更加强一些,或者甚至于有些区块联接在一起。
三、组件内部管理
组件内部管理是指将骨架屏封装至各个组件中,由组件自己管理。当页面处于加载状态时,给须要显示骨架屏的组件传入特定值来标示当前组件须要展示骨架屏状态,然后在组件内部添加相应的class,从而通过款式文件再给特定的class添加骨架屏疗效。
我们以antd的Card组件为例:
#2:a:f:6:5:7:5:5:b:9:3:d:c:c:7:9:5:0:9:8:d:d:3:2:f:d:e:4:4:8:1:2#
当页面还处于加载状态时,antd将loading设置为true,下面来看一下Card组件的实现:
#5:f:9:a:7:8:a:a:6:7:f:4:d:4:9:6:1:d:e:a:8:b:8:b:d:a:9:f:6:a:c:3#
源码请见:#L187
#8:8:e:2:c:5:7:c:0:1:b:7:b:9:1:4:d:8:f:4:b:3:e:d:2:c:a:8:7:d:a:5#
源码请见:#L257
对于一些不太复杂的项目这些方案无疑会降低好多工作量,与此同时组件还须要接收传入的特定标示来判定组件是否须要展示骨架屏,这种方法对组件的入侵性也比较严重,对于组件而言也比较繁杂。
四、 图像辨识
在Code Review的时侯,同事突发奇想说“如果能用图象辨识来辨识区域,然后生成骨架屏那就好了”,于是第二天我就开始研究这些方法的可行性,你别说还真行!目前图象辨识这些方法使用的是NodeJS版的OpenCV来实现。我们先以腾讯大会小程序的界面为例:
#1:b:7:9:5:4:c:d:4:f:a:4:2:8:3:1:6:2:d:b:0:0:f:7:a:0:8:e:8:4:c:e#
然后我们使用OpenCV来勾画并相应区域疗效:
#8:c:2:e:2:7:b:3:8:7:0:f:8:a:5:5:1:7:9:4:c:6:4:7:9:6:b:2:5:3:a:5#
实现的疗效不好,但是至少说明这些方法是行得通的!如要勾画的愈发精细确切,那么接下来的工作就是调参了。先来看一下实现:
#f:5:d:3:1:d:7:c:9:2:9:8:5:d:3:4:1:4:3:d:e:f:b:d:3:0:0:1:2:e:f:4#
代码注释写的还是比较详尽的,就不在此赘言,如有疑惑可查阅OpenCV相关文档。
这种方法尽管很方便,但在使用图象辨识的形式时,前提是我们有图象,假如我们使用原型图作为图象辨识的图象来源,那么勾画出的图象应当是与原型图比列一致的图象。假如原型图是基于iPhone6/7/8设计的,那么当我们勾画出的图象在iPhoneX(及其他码率的手机)上显示时可能会发生形变。
结束语
上述四种形式各有利弊,具体使用哪一种还是要看项目倾向以及团队精力来合理选择。引入静态文件的不见得是最繁琐的,当然图象辨识也不一定是最安逸的。并且上述方案也不仅限于小程序项目,其它方式的项目也可参考。
本次分享到此结束,本文没有彩蛋,感谢阅读!
#e:3:d:e:d:6:a:d:6:3:a:3:a:c:8:c:6:0:5:f:0:8:6:3:e:2:c:0:8:0:3:8#
小彩蛋我们的故事如何会戛然而止!如果你要问我:“你们项目究竟使用的哪一种?”
#2:b:6:9:d:7:a:1:1:2:2:9:f:4:a:9:e:f:7:5:e:4:5:9:2:e:c:1:c:9:1:c#
我们准备交给UI vendor来负责,不愧是我们,哈哈哈哈哈哈哈哈哈!!!