Skeleton Screen骨架屏

背景

传统的优化用户等待的体验 - 白屏使用动画,菊花图,加载进度条等:


image.png

It can be bad because progress indicators by definition call attention to the fact that someone needs to wait.
It’s like watching the clock tick down -when you do, time seems to go slower.

简介

骨架屏是在2015年由Luke提出的一种全新的优化用户等待体验的方案,它可以被视为是原来加载菊花图的一种升级版。
骨架屏可以理解为是当数据还未加载进来前,页面的一个空白版本。

image.png

下面的示例图是主流网站骨架屏实例。相比于传统的菊花图,骨架屏会在感官上觉得内容出现的流畅而不突兀,体验更加优良。


image.png

如下图所示,从左到右依次是Profile页面从初始化到最终content渲染完毕。
在页面完全渲染完成之前,用户会看到一个样式简单,描绘了当前页面的大致框架的骨架屏页面,然后骨架屏中各个占位部分被实际资源完全替换,这个过程中用户会觉得内容正在逐渐加载即将呈现,降低了用户的焦躁情绪,使得加载过程主观上变得流畅。


image.png

Immutable regions of a page are rendered instantly on load, appearing as neutral color blocks, and are gradually replaced with content such as images, headings, and interface labels.
This creates the sense that things are happening immediately as information is incrementally displayed on the screen.

骨架屏的生成方式

1. 手写骨架屏

自定义 样式/ 使用 插件,用来把我们写的骨架屏代码插入到页面模板的挂载点中,完成骨架屏的注入


image.png

2. 自动生成并插入静态骨架屏

以page-skeleton-webpack-plugin(饿了么开源的插件 )为例,它根据项目中不同的路由页面生成相应的骨架屏页面,并将骨架屏页面通过 webpack 打包到对应的静态路由页面中:

* Step1. 启动Puppeteer并打开要生成骨架屏的页面
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];
const { Skeleton } = require('page-skeleton-webpack-plugin');
let skeleton = new Skeleton();

(async () => {
    const browser = await (puppeteer.launch({
        timeout: 15000, //设置超时时间
        ignoreHTTPSErrors: true, //如果是访问https页面 此属性会忽略https错误
        devtools: true, // 打开开发者工具, 当此值为true时, headless总为false
        headless: false  // 非headless模式,为了能直观看到页面生成骨架屏的过程
    }));

    const page = await browser.newPage();
    await page.emulate(iPhone); // 因为是移动端,设置模拟iphone6
    await page.goto(‘XXX’); // 打开需要生成骨架屏的XXX网站
    await page.waitForSelector('.YYYY'); // 等待首屏加载完成
    await skeleton.makeSkeleton(page); // 开始build骨架屏
})();
* Step2. 构建骨架页面

在打开的页面进行 CSS 样式的覆盖,对元素进行增减,来生成骨架页面。

// page-skeleton-webpack-plugin/src/skeleton.js
async makeSkeleton(page) {
    const {defer} = this.options
    // 脚本路径在page-skeleton-webpack-plugin/src/script/index.js
    await page.addScriptTag({content: this.scriptContent}) // 把生成骨架屏代码注入puppeteer同时执行初始化
    await sleep(defer) // 延迟逻辑,用于等待某些异步操作
    await page.evaluate((options) => { // 在当前打开的页面实例上下文中执行方法
      Skeleton.genSkeleton(options) // 执行genSkeleton方法生成骨架屏
    }, this.options)
}
genSkeleton() -
image.png

image.png
  1. 将分类好的文本块,按钮块等处理生成骨架结构代码
    将页面划分为不同的块,然后分别对每个块进行处理,这样不会破坏页面整体的样式和布局。
    当我们最终生成骨架屏后,骨架屏的布局样式将和真实页面的布局样式完全一致,这样就达到了复用样式及页面布局的目的。
image.png
附: 初始化参数
image.png

image.png
* Step3. 根据 Puppeteer 渲染的骨架页面获取HTML 和 CSS
function getHtmlAndStyle() {
   const root = document.documentElement
   const rawHtml = root.outerHTML
   const styles = Array.from($$('style')).map(style => style.innerHTML || style.innerText)
   // other code
   const cleanedHtml = document.body.innerHTML
   return { rawHtml, styles, cleanedHtml }
}

总结:

通过 puppeteer 打开需要生成骨架屏的页面,
等待页面加载渲染完成之后,在保留页面布局样式的前提下,
对页面中元素进行删减或增添,对已有元素通过层叠样式进行覆盖,使得其展示为灰色块。
然后将修改后的 template和样式code提取出来,这样就生成骨架屏了。

image.png

Reference

https://uxdesign.cc/what-you-should-know-about-skeleton-screens-a820c45a571a
https://css-tricks.com/building-skeleton-screens-css-custom-properties/
https://github.com/dvtng/react-loading-skeleton
https://github.com/ElemeFE/page-skeleton-webpack-plugin
https://www.lukew.com/ff/entry.asp?1797
https://medium.com/mobify-design-team/designing-for-the-appearance-of-speed-aaabc7f568c2
https://blog.logrocket.com/improve-ux-in-react-apps-by-showing-skeleton-ui/
https://juejin.im/post/5c9890166fb9a070b8506341#heading-0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容