activex for chrome扩展程序 下载”_Chrome扩展程序一键生成网页骨架屏

对于依赖接口渲染的页面,在拿到数据之前页面往往是空白的,为了提示用户当前正在加载中,往往会使用进度条、loading图标或骨架屏的方式。对于前两种方案而言,实现比较简单;本文主要研究骨架屏的应用及实现,并给出一种使用Chrome扩展工具快速生成骨架屏的方案。

首先看看效果 先放一个动图展示

d3452a303a684ea29f2b2bca200383d1.gif

掘金首页

5265087307e7c2cc52d8775be8248590.png

百度首页

f8f29e656ce4158fa07d6e21a9cc9e4f.png

知乎首页

957124a6c8490a2dd60af12e54b0500d.png

安装插件后访问任意网页,基本上均可以一键生成骨架屏(由于时间关系,部分节点并没有完全实现,不过目前展示Demo应该是够了

本文所有代码均放在https://github.com/tangxiangmin/web-skeleton-extension上面了,时间关系代码写的比较潦草~

(写完准备提交Chrome应用商店的时候才发现,居然有一个类似的应用:skeleton extention~囧,就当瞎折腾一番了

骨架屏方案

目前有几种比较常见的骨架屏方案

  1. 使用图片、SVG实现骨架屏效果,可以让设计在提供页面设计稿的时候同步一份当前页面的骨架屏图片资源,这种方案开发成本较低,缺点是不太灵活,如果页面迭代比较频繁,会导致设计同事的工作量增大
  2. 手写HTML+CSS实现骨架屏,较第一种图片方案而言,可以更灵活地定制骨架屏UI和动画效果,缺点也很明显,开发和维护成本都较高;目前可以使用诸如react-content-loader等现有的骨架屏库,通过配置快速生成对应的骨架屏,但对于某些高度定制的页面也不能很好地满足业务需求
  3. 饿了么前端团队提供的一套方案:page-skeleton-webpack-plugin,其大致原理是使用puppeteer向指定页面注入代码,遍历页面节点,根据节点的类型渲染对应的骨架屏样式,然后结合webpack将返回的HTML自动注入到项目模板文件中,传送门:一种自动化生成骨架屏的方案:https://github.com/Jocs/jocs.github.io/issues/22

在最开始调研骨架屏方案的时候也注意到了page-skeleton-webpack-plugin,体验了一下发现并不能完全满足我的需求,对于骨架屏方案,我的期望是

  • 使用方便,能够根据给定页面,自动生成对应的骨架屏,不需要安装额外的工具(不用安装puppeteer)
  • 能够灵活地处理骨架屏生成的HTML文件,支持首屏或者部分区域使用骨架屏(不需要webapck自动注入模板文件,反之,需要实现为项目单个或多个页面配置对应骨架屏)
  • 能够自定义骨架屏各个区块的样式,能够指定包含或者忽略某些节点,生成的代码体积要足够小(骨架屏并不需要完全还原原本页面)

恰好调研骨架屏方案的那段时间正在处理Chrome扩展程序的工单,发现page-skeleton-webpack-plugin借助puppeteer执行页面脚本的方案,完全可以通过扩展程序的content.js实现,这样,不需要借助本地开发环境也可以渲染骨架屏了

骨架屏实现原理

何为骨架?

首先需要保留节点的布局信息,这样就可以复用节点原有的样式,不会破坏整体布局,最后生成的骨架屏样式就可以与原本的页面保持一致。

然后为了保证生成样式的统一,需要覆盖节点原本的UI信息,包括背景色、图片、边框等。

由于骨架屏仅仅作为数据返回之前的占位,并不需要完全复原原本的页面,因此为了使整个骨架屏看起来比较简洁,可以忽略不必要的节点。

那么要处理哪些节点、忽略哪些节点呢?因此针对不同节点,我们需要进行判断并分别处理。

区分不同节点

这部分大量参考了一种自动化生成骨架屏的方案:https://github.com/Jocs/jocs.github.io/issues/22 这篇文章的思路,不妨移步阅读,下面简单整理一下各种不同节点的处理方案,并增加了一些额外的节点处理策略。

由于涉及大量的节点操作,因此使用了jQuery

文字

把文字占据的空间看做上行高、内容、下行高,

  • 行高部分用透明色,
  • 内容部分用灰色

这样就可以展示原本的文字区域了,对于多行文字而言,显示的就是斑马状条纹形式的骨架屏结构

.line {
    background-image: linear-gradient(red 25%,blue 25%, blue 75%, red 75%);
    /*background-image: linear-gradient(red 25%,blue 0, blue 75%, red 0); // 与上面等价*/
}
复制代码

这里需要计算元素的行高、字体大小等信息

renderText($dom) {
    let fontSize = parseFloat($dom.css("font-size"));
    let lineHeight = $dom.css("line-height");

    // todo 处理浏览器默认行高、包含继承、自定义等属性
    if (lineHeight === "normal") {
        lineHeight = fontSize * 1.4;
    } else {
        lineHeight = parseFloat(lineHeight);
    }

    const textHeightRatio = fontSize / lineHeight;
    const firstColorPoint = (((1 - textHeightRatio) / 2) * 100).toFixed(2);
    const secondColorPoint = (((1 - textHeightRatio) / 2 + textHeightRatio) * 100).toFixed(2);

    const style = `--fp:${firstColorPoint}%;--sp:${secondColorPoint}%;--lh:${lineHeight}px;`;
    $dom.addClass('sk-text');
    $dom.attr("style", style);
}
复制代码

因为每个文本节点的字体大小和行高可能不一样,如果全在style标签上添加样式,则生成的HTML文件可能比较大,因此这里使用了CSS Var,然后统一在sk-text的样式类中处理背景色

.sk-text {
    --c: #eee;
    --fp: 0%;
    --sp: 0%;
    --lh: 0;

    display: inline-block;
    background-origin: content-box !important;
    background-clip: content-box !important;
    background-color: transparent !important;
    background-repeat: repeat-y !important;
    background-image: linear-gradient(transparent var(--fp), var(--c) 0, var(--c) var(--sp), transparent 0);
    background-size: 100% var(--lh);
    color: transparent !important;
}
复制代码

图片

获取原始图片节点的宽高,然后使用1像素的base64灰色图片替换原始节点的src属性

renderImg($img) {
    let width = $img.width()
    let height = $img.height()
    // 一像素灰色图片
    let emptyImage = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
    $img.attr("src", emptyImage);

    $img.css({
        background: "#eee",
        width: width + "px",
        height: height + "px"
    })
}
复制代码

对于包含背景图片的区块而言,只需要将其背景覆盖成灰色即可。

按钮、input

使用灰色背景的块占据

边框

替换对应边框的颜色为骨架屏灰色

列表

列表元素是骨架屏中一个比较常见的区块,列表元素可以由上面这些区块组成,但是为了保证生成规整的骨架屏,采取的策略是使用移除多余的元素,使用第一个元素克隆占位

function renderList($dom) {
    $dom.addClass("sk-list")

    let $children = $dom.children()
    let $child = $children.first()
    let len = $children.length

    // 列表元素子节点统一,保证页面骨架整齐
    for (let i = 1; i         $children.eq(i).remove()
    }
    for (let i = 1; i         let tmp = $child.clone(true)
        $dom.append(tmp)
    }
}
复制代码

遍历DOM树

当确定了不同类型节点的处理策略之后,就可以从入口节点遍历整个DOM树执行处理方法了

function preorder($dom) {
    // ...获取节点类型,执行相关策略放啊
    
    // 遍历子节点
    $dom.children().each(function () {
        const $this = $(this)
        preorder($this)
    });
}
复制代码

在遍历期间还需要处理一些特殊情况

  • 不可见的元素及其子节点应该停止遍历
  • 对于某些节点而言,可能存在一些定制操作
    • 由用户指定节点类型,而不是默认根据节点nodeType推断
    • 忽略某些节点的处理,如一个ul标签我们可能并不希望将其转换成列表
    • 直接隐藏节点,如某些小图标等,为了保证骨架屏简洁,可能需要直接隐藏节点

基于这些场景,引入了skeleton-type的概念,对应上面章节提到的节点类型,在遍历时会优先读取节点上的该属性值,只有当属性值不存在时,才会根据nodeType自行推断相关的类型;

  • 对于需要隐藏的节点,直接指定skeleton-typeignore
  • 对于忽略节点类型而言,则使用skeleton-exclude-type
 let type = $dom.attr(KEY) || getNodeSkeletonType($dom)  // 自动检测节点类型,并附上type
let excludeType = $dom.attr(KEY_EXCLUDE)

if (!excludeType || type !== excludeType) {
    let handlers = {
        [TEXT]: renderText,
        [IMAGE]: renderImg,
        [BLOCK]: renderBlock,
        [BORDER]: renderBorder,
        [BUTTON]: renderButton,
        [LIST]: renderList,
        [BACKGROUND_IMAGE]: renderBackgroundImage,
        [INPUT]: renderInput,
        [IGNORE]: renderIgnore
    }

    let handler = handlers[type]
    handler && handler($dom)
}
复制代码

这样相当于暴露了skeleton-typeskeleton-exclude-type两个HTML属性,在开发页面的时候就可以直接指定骨架屏区块类型,方便定制业务需要的骨架屏。

当然逐个去配置skeleton-type也会显得比较繁琐,在getNodeSkeletonType中会尽可能地推断并生成比较符合要求的骨架屏效果;此外,基于Chrome扩展程序的工具还暴露了一个配置参数

 renderSkeleton("body", {
    ignore: '',
    selector: {
        [key]: {include: '', exclude: ''}
    },
})
复制代码

用掘金首页试一下

原本的效果

b1c8b59567f66f8bd21b6051e02431c7.png

如果不过滤的话,因为顶部导航栏使用的是ul,会默认识别成list,导致展示出现问题

a717c4dfc64b81eb994b102615cb70a8.png

自定义配置后的效果

{
    ignore: ['.banner .label'].join(','), // 隐藏右侧广告栏的提示按钮
    selector: {
        block: {
            // 将表单转换成一个BLOCK
            include: ['.add-group .more', '.search-form'].join(',')
        },
        list: {
            // 排除导航栏的ul标签
            exclude: ['.nav-list'].join(',')
        },
    },
}
复制代码
5265087307e7c2cc52d8775be8248590.png

此外,我们也可以只传入某个节点而非body的选择器,这样就只会生成对应节点的HTML

将骨架屏嵌入应用

骨架屏有两种比较常见的应用场景

  • 用于首屏或某个页面打开时等待数据返回时的占位
  • 用于滚动加载等列表页面占位

接下来研究在这两种场景下如何嵌入骨架屏

获取骨架屏代码

在前面遍历入口节点DOM树渲染骨架屏之后,我们只需要获取对应节点的HTML代码即可。由于没有修改原本布局信息,因此这段HTML代码再同一个页面是完全可以展示的。我们要做的就是拿到对应的骨架屏HTML代码。

由于整个操作都是在当前页面上执行的,拿到当前页面上某个节点的的html内容就变得十分简单了

  • 通过控制台,找到节点然后访问innerHTML
  • 通过调试工具Elements面板,然后Copy HTML

上面的方式需要手动去操作,我们可以使用扩展程序在骨架屏渲染结束之后自动保存对应的HTML代码

// chromeMsg是自己封装的一个`chrome.runtime.onMessage`工具
chromeMsg.on("createSkeleton", (params) => {
    const {config} = params
    // 默认页面根节点,可以导出某个dom容器的骨架屏结构
    let content = renderSkeleton(".page", config)
    // 处理对应的骨架屏HTML代码,这里只是简单打印,也可以直接保存文件到本地,或者通过HTTP接口调用某些钩子服务完成自动注入等功能
    console.log(content)
})
复制代码

整页应用

对于整页骨架屏,我们需要先使用测试数据生成对应的骨架屏,然后将得到的HTML放入页面中即可。

以Vue等单页项目而言,在整个应用初始化之前,页面上只存在根节点,用户看见的是一片空白,对于这种首屏应用,我们可以将得到的骨架屏代码直接嵌入到根节点中,这样当页面加载至应用初始化之前,用户都能看见完整的骨架屏效果。

904bf3753bbd70c36239646bfb472bf4.gif

对于其他单页应用其他页面而言,也可以采用类似的原理,等待接口返回前展示骨架屏

<SkeletonFrame :frame="html" :loading="isLoading">
    <RealPage>RealPage>
SkeletonFrame>
复制代码

比如我们可以封装一个SkeletonFrame组件,其接口包括

  • frameloading两个props,表示骨架屏HTML和是否正在加载,当loading为true时展示骨架屏
  • default slot需要真实渲染的页面组件,当loading为false时展示真正页面组件

局部骨架屏

对于一些需要持续加载的数据,如滚动加载等,可以先获取对应区块的骨架屏HTML代码(不需要整个页面的骨架屏代码),然后将其封装成待展示组件,与上面SkeletonFrame逻辑比较类似

小结

本文整理了骨架屏的实现原理,然后提供了一种通过Chrome扩展程序生成任意网页的骨架屏方案,主要包括

  • 将页面按节点类型拆分成不同区块
  • 支持自定义节点类型、忽略或隐藏节点
  • 导出并使用骨架屏HTML代码

当然,上面只是介绍了大概的实现思路,并给出了一个简单的Demo,距离实际应用还有一些距离,等后面有时间再进一步完善吧。本文所有代码均放在github上面了,欢迎提PR和issue。

近期程序员都喜欢用的架构图工具

vue+ts打造一个酷炫的星空聊天室

13ad6dd018a1c9808fe352a98a2f1244.png若此文有用,何不素质三连❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

dynamo python修改多个参数_python之函数

a.sort()没有返回值。而sorted(a)是有返回值的。Python的标准比较运算符&#xff1a;<、<、 > 、>、 、 !函数用法和底层分析&#xff1a;函数是一个可重用的程序代码块&#xff0c;函数也代表一个任务和功能&#xff08;function&#xff09;,是代码复用的通用机制…

javascript进制转换_JavaScript 加减危机——为什么会出现这样的结果?

在日常工作计算中&#xff0c;我们如履薄冰&#xff0c;但是 JavaScript 总能给我们这样那样的 surprise~0.1 0.2 &#xff1f;1 - 0.9 &#xff1f;如果小伙伴给出内心的结果&#xff1a;0.1 0.2 0.31 - 0.9 0.1那么小伙伴会被事实狠狠地扇脸&#xff1a;console.log(0.…

java实现复制粘贴的计算器_软帝学院教你用java编写计算器(三)

教你用java编写计算器(三)import java.awt.Color;import java.awt.Dimension;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JMenu;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax…

php 公众号验证回调方法_如何进行公众号文章收集 两种收集方法详解

大家都知道优质的公众号吸引用户最关键的就是要优质的文章&#xff0c;所以会有专人负责进行公众号文章收集工作&#xff0c;下面我们跟随拓途数据一起来了解一下如何进行公众号文章收集的相关资料吧。 如何进行公众号文章收集方案一&#xff1a;基于搜狗入口 在网上能搜索到的…

mysql保存一个文件怎么打开_悄悄告诉你,MySQL 通过SQL语句导出到Excel的方法-sql文件怎么打开...

执行SQL语句select fullname,time,endtime,closed from chat_archive into outfile c:/xxx.xls注意&#xff1a;因为office默认的是gb2312编码&#xff0c;服务器端生成的很有可能是utf-8编码&#xff0c;此时有几种选择1、把查询出来的结果转换为GB2312格式(字段fullname)sele…

gerber文件怎么导贴片坐标_SMT贴片机在线编程调试

SMT贴片机分为离线编程和在线编程调试&#xff0c;在线编程调试就是在 SMT 贴片机上对离线编程的程序进行优化调试编辑。SMT 贴片机在线编程调试总体上就是两个步骤&#xff0c;一个是离线编程的程序进行编程&#xff0c;然后就是总体检查并备份到贴片机电脑内。一、在 SMT 贴片…

java销售额查询_用JSP+JavaBean开发模式实现一个销售额的查询

数据库使用mysql&#xff0c;如下&#xff1a;vo包的Sales类&#xff1a;package com.vo;public class Sales {public String salestime;public float salesnum;public String getSalestime() {return salestime;}public void setSalestime(String salestime) {this.salestime …

spss数据_怎么建立SPSS数据库、录入数据?

怎么把收集的问卷、测试数据等原始资料转变为“SPSS数据库”&#xff1f;数据包括离散&#xff08;单选题、多选题等&#xff09;、连续&#xff08;年龄、身高、肺活量、人数等&#xff09;两类。以下面四个题目为例&#xff0c;介绍采用SPSS建立数据库的方法&#xff1a;A2.学…

php进度条如何计算,投票最后显示进度条的百分比怎么算

我自己写的一个投票结果显示&#xff0c;其中设定票数最多的那个进度条为100&#xff05;。public class voteresult : System.Web.UI.Page{protected System.Web.UI.HtmlControls.HtmlTableCell td_vote;protected System.Web.UI.HtmlControls.HtmlTable tab_result;private v…

函数的返回值可以不用赋值_C语言学习|函数的应用《一》

C语言为程序的结构提供了函数和模块一、函数的定义与使用《编程之道》中写道&#xff1a;“一个程序应该是灵活自由的、它的子过程就像串在一根线子上的珍珠。”子过程在C语言中被称为”函数“。程序的执行从主函数开始&#xff0c;往复、循环、迭代地调用一个又一个函数。函数…

php的yii框架配置,php配置yii框架_PHP教程

个人爱好&#xff0c;研究了下php的yii框架。首先&#xff0c;研究yii框架的前提是下载php的一键安装和zend studio.. php的一键安装给出连接如下http://www.download3k.com/Install-XAMPP.html 。。。zend studion的链接如下http://www.zend.com/en/products/studio/downloads…

蚂蚁庄园 php源码,蚂蚁庄园五体投地

蚂蚁庄园五体投地&#xff0c;蚂蚁庄园小课堂的题目你答对了吗&#xff1f;今天题目有点难&#xff0c;大家可能不清楚&#xff0c;人们常常会对极其敬重的人五体投地&#xff0c;五体投地的五体指的是什么意思呢&#xff1f;“五体”又称“五轮”&#xff0c;指双肘、双膝和额…

电脑端二维码识别工具_电脑端自签工具更新,多功能软件一键签名

因为苹果后台的调整&#xff0c;电脑端的自签工具 Cydia Impactor 一直无法使用&#xff0c;如今虽然没有等到大胡子对 Cydia Impactor 适配更新&#xff0c;却等到了全新的替代工具。先说下为什么 Cydia Impactor 为什么让那么多人惦记&#xff0c;虽然对于不越狱安装越狱工具…

python列表是顺序表还是链表_顺序表与链表

Python中的顺序表 Python中的list和tuple两种类型采用了顺序表的实现技术&#xff0c;具有前面讨论的顺序表的所有性质。 tuple是不可变类型&#xff0c;即不变的顺序表&#xff0c;因此不支持改变其内部状态的任何操作&#xff0c;而其他方面&#xff0c;则与list的性质类似。…

js for foreach 快慢_js基本搜索算法实现与170万条数据下的性能测试

前言今天让我们来继续聊一聊js算法&#xff0c;通过接下来的讲解&#xff0c;我们可以了解到搜索算法的基本实现以及各种实现方法的性能&#xff0c;进而发现for循环&#xff0c;forEach&#xff0c;While的性能差异&#xff0c;我们还会了解到如何通过web worker做算法分片&am…

iterm php,iTerm2笔记

本文是 iTerm2 的使用笔记&#xff0c;不定期更新。1 注释说明对于 Preferences 的修改&#xff0c;> 表示需要切换选项卡&#xff0c;-> 表示在同一选项卡内2 参考3 杂301 如何随时随地一键调用 Quake-like iTerm2首先声明&#xff1a;由于 Mac OS 本身对窗体「最大化」…

matlab emd功率谱密度,【脑电信号分类】脑电信号提取PSD功率谱密度特征

脑电信号是一种非平稳的随机信号&#xff0c;一般而言随机信号的持续时间是无限长的&#xff0c;因此随机信号的总能量是无限的&#xff0c;而随机过程的任意一个样本函数都不满足绝对可积条件&#xff0c;所以其傅里叶变换不存在。不过&#xff0c;尽管随机信号的总能量是无限…

组装服务器配置清单_2020年组装电脑配置清单列表

随着电脑技术的不断革新&#xff0c;越来越多的家庭都有各式各样的电子设备。而电脑现在基本上是家家都有的物品&#xff0c;可是在购买电脑的时候新手小白需要注意那些事项呢&#xff1f;今天我们就给告诉小白如何组装电脑以小白组装电脑配置清单。1、购买电脑&#xff0c;您首…

oracle 关于归档的视图,oracle 与归档日志相关的几个视图

归档日志占据的数据库举足轻重的位置&#xff0c;以下系统视图来了解归档日志情况V$ARCHIVEV$ARCHIVED_LOG 已归档日志详单V$ARCHIVE_GAP 归档日志丢失V$ARCHIVE_PROCESSES 归档进程信息V$ARCHIVE_DEST 查看备份路径情况V$ARCHIVE_DEST_STATUSv$recovery_f…

count数据库优化oracle,迷惑性SQL性能问题排查与优化

&#xff1a;数据科学、人工智能从业者的在线大学。数据科学(Python/R/Julia)数据分析、机器学习、深度学习作者简介戴秋龙&#xff0c;拥有超过八年的电信、保险、税务行业核心系统ORACLE数据库优化&#xff0c;优化经验&#xff0c;具备丰富的行业服务背景。对Oracle数据库有…