来源:花满楼
3359 Zhan.zhi Hu.com/p/74403911
什么是骨架屏?
什么是骨架屏幕? 骨架屏幕(Skeleton Screen )是指在页面数据加载完成之前,向用户显示页面的大致结构(灰色占位图),在获得界面数据后渲染实际页面内容Skeleton Screen是近两年开始流行的负载控制,本质上是接口加载中的过渡效应。
如果能在加载前提前显示网页的大致轮廓,然后逐渐加载真实内容,既能降低用户的焦灼感,又能使界面加载过程自然顺畅,避免网页长时间洁白或闪烁这就是Skeleton Screen!
Skeleton Screen可以给人一种页面内容“已经部分呈现”的印象,与传统的loading效果相比,可以在一定程度上改善用户体验。
骨架屏的实现方案
目前,生成骨架屏幕的技术方案大致有三种。
1 .图片、svg或手动编写骨架屏代码:使用HTML CSS的方法可以很快达到骨架屏的效果,但面对视觉设计改版和需求更迭,骨架屏的后续修改非常重要这种机械化的重复性劳动方式此时看起来有点机动性不足。
2 .通过渲染手写的代码生成合适的骨架屏幕。 该方案创建了比较成熟的vue-skeleton-webpack-plugin,通过vueSSR结合webpack渲染构建时创建的vue骨架屏幕组件,没有渲染生成的DOM
//web pack.conf.jsconstskeletonwebpackplugin=require (vue-skeleton-web pack-plugin ); plugins : [//. newskeletonwebpackplugin ({ web pack config : ) entry: ) app : resolve./src/entry-skeee 我们将预渲染生成骨架屏幕所需的DOM节点,但由于此方案与vue相关技术直接相关,因此在当前前端框架将世界一分为二的大型环境中,可能需要更灵活、更可控的方案。
3 .饿了么内部生成骨架页面的工具:该方案通过webpack插件page-skeleton-webpack-plugin方式无缝集成到项目开发中,非常强大的骨架界面自动生成另外,生成的骨架节点基于页面本身的结构和CSS,有时嵌套很深,体积不太小,只支持history模式。
//web pack.conf.jsconsthtmlwebpackplugin=require (html-web pack-plugin ) (const ) skeletonplugin )=require ) constpath=require(path ) ) plugins : (/. newhtmlwebpackplugin/yourhtmlwebpackpluginconfig )、newskeletonplugin ` ${customPath} ` )、//用于存储shell文件的地址staticdir3360path output.path `所在的routes:['/'、'/search']、/
之后仔细想想,骨架屏幕不就和由很多颜色块组合而成的页面一样吗? 与现有的骨架屏幕方案比较,感觉这个想法有点像“捷径”。 进一步思考,这些色块与其基于现在的页面分析生成节点,不如由JS分析页面节点,通过DOM操作生成色块并组装到框架屏幕上。 那么,该如何准确地分析页面节点,根据节点生成什么样的颜色块呢?
由于框架屏幕表示页面的大致结构,因此必须首先用js分析页面的结构。 在分析之前,必须创建规则以确定需要排除哪些节点。 需要生成颜色块的节点类型是什么? 生成的颜色块如何排列等。 我们最初决定的规则如下
1 .仅扫描可见区域中显示的DOM节点,如下所示:
隐藏元素、宽度大于0的元素、非透明元素、内容非空格的元素、浏览窗口显示区域中的元素等;
2 .为图像、文本、表格项、音频、Canvas、定制特征块等区域产生颜色块;
>3. 页面节点使用的样式不可控,所以不可取 style 的尺寸相关的值,可通过 getBoundingClientRect 获取节点宽、高、距离视口距离的绝对值,计算出与当前设备的宽高对应的百分比作为颜色块的单位,来适配不同设备;基于这套规则,我们开始生成骨架屏:
首先,确定一个 rootNode 作为入口节点,比如 document.body,同时方便以后扩展到生成页面内局部的骨架屏,由此入口进行递归遍历和筛选,初步排除不可见节点。
function isHideStyle(node) { return getStyle(node, 'display') === 'none' || getStyle(node, 'visibility') === 'hidden' || getStyle(node, 'opacity') == 0 || node.hidden;}接下来判断元素特征,确定是否符合生成条件,对于符合条件的区域,”一视同仁”生成相应区域的颜色块。”一视同仁”即对于符合条件的区域不区分具体元素、不考虑结构层级、不考虑样式,统一根据该区域与视口的绝对距离值生成 div 的颜色块。之所以这样是因为生成的节点是扁平的,体积比较小,同时避免额外的读取样式表、通过抽离样式维持骨架屏的外观,这种统一生成的方式使得骨架屏的节点更可控。基于那上述“走捷径”的想法,该方法生成的骨架屏是由纯 DOM 颜色块拼成的。
生成颜色块的方法:
const blocks = [];// width,height,top,left 都是算好的百分比function drawBlock({width, height, top, left, zIndex = 9999999, background, radius} = {}) { const styles = [ 'position: fixed', 'z-index: '+ zIndex, 'top: '+ top +'%', 'left: '+ left +'%', 'width: '+ width +'%', 'height: '+ height +'%', 'background: '+ background ]; radius && radius != '0px' && styles.push('border-radius: ' + radius); // animation && styles.push('animation: ' + animation); blocks.push(`<div style="${ styles.join(';') }"></div>`);}绘制颜色块并不难,绘制之前的分析确认才是这个方案真正的核心和难点。比如,对于页面结构比较复杂或者大图片比较多的页面,由图片拼接的区域没有边界,生成的颜色块就会紧挨着,出现不尽如人意的地方。再比如,一个包含很多符合生成条件的小块的 card 块区域,是以 card 块为准还是以里面的小块为准来生成颜色块呢?如果以小块为准,绘制结果可能奋斗的羊压根就不是一个 card 块,再加上布局方式和样式的可能性太多,大大增加了不确定因素。而如果以 card 块为准生成颜色块的话还要对 card 块做专门的规则。
目前来说,对于页面结构不是特别复杂,不是满屏图片的,不是布局方式特别“飘逸“的场景,该方式已经可以生成比较理想的骨架屏了。而对于那些与预期相差较远的情况,我们提供了两个钩子函数可供微调:
1. init 函数,在开始遍历节点之前执行,适合删除干扰节点等操作。
2. includeElement(node, draw) 函数,可在遍历到指定节点时,调 用 draw 方法进行自定义绘制。
通过以上步骤就能够直接在浏览器中生成骨架屏代码了。
在浏览器里运行
由于我们的方案出发点是通过单纯的 DOM 操作,遍历页面上的节点,根据制定的规则生成相应区域的颜色块,最终形成页面的骨架屏,所以核心代码完全可以直接跑在浏览器端;
const createSkeletonHTML = require('draw-page-structure/evalDOM') createSkeletonHTML({ // ... background: 'red', animation: 'opacity 1s linear infinite;' }).then(skeletonHTML => { console.log(skeletonHTML) }).catch(e => { console.error(e) })结合 Puppeteer 自动生成骨架屏
虽然该方式已经可以生成骨架屏代码了,但是还是不够自动化,为了让生成的骨架屏代码自动加载进指定页面。于是,我们开发了一个配套的 CLI 工具。这个工具通过 Puppeteer 运行页面,并把 evalDOM.js 脚本注入页面自动执行,执行的结果是生成的骨架屏代码被插入到应用页面。
我们的方案大概思路如下:
接下来看看如何使用 CLI 工具生成骨架屏,最多只需如下四步:
1. 全局安装,npm i draw-page-structure – g
2. dps init 生成配置文件 dps.config.js
3. 修改 dps.config.js 进行相关配置
4. dps start 开始生成骨架屏
只需简单几步,然而并没有繁琐的配置:
一般来说,你需要按自己的项目情况来配置 dps.config.js ,常见的配置项有:
url: 待生成骨架屏的页面地址
output.filepath: 生成的骨架屏节点写入的文件
output.injectSelector: 骨架屏节点插入的位置,默认 #app
background: 骨架屏主题色
animation: css3 动画属性
rootNode: 真对某个模块生成骨架屏
device: 设备类型,默认 mobile
extraHTTPHeaders: 添加请求头
init: 开始生成之前的操作
includeElement(node, draw): 定制某个节点如何生成
writePageStructure(html, filepath): 回调的骨架屏节点
详细代码及工具的使用请移步 [Github]( famanoder/dps);
初步实现的效果:
* 京东 PLUS 会员正式中首页:
* 京东 PLUS 会员正式中首页,通过该方案生成的骨架屏效果:
* 移动端百度首页,通过该方案生成的骨架屏效果:
总结
以上就是基于 DOM 的骨架屏自动生成方案,其核心是 evalDOM 函数。这个方案在很多场景下的表现还是令人满意的。不过,网页布局和样式组合的可能性太多,想要在各种场景下都获得理想的效果,还有很长的路要走,但既然已经在路上,就勇敢的向前吧!
欢迎 star,欢迎提 PR !https://github.com/famanoder/dps
最后如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
欢迎加我微信「qianyu443033099」拉你进技术群,长期交流学习...
关注公众号「前端下午茶」,持续为你推送精选好文,也可以加我为好友,随时聊骚。
点个在看支持我吧,转发就更好了