使用CSS自定义属性实现骨架屏效果


Posted in HTML / CSS onJune 21, 2022

前言

其实这篇文章网上已经有翻译版本,但是读起来明显是机翻的,实在是受不了,于是就用自己的理解翻译了一下

正文

项目要不要加载loading状态通常是在项目完成后才考虑的事情,当然,有时候直接就不考虑了。

开发人员的职责不只是提高性能,同时优化网络差时,请求接口缓慢导致的页面的慢渲染也是非常重要的。

速度的错觉

随着我们对移动体验的期望的变化,我们对性能的理解也在变化。我们期望,无论当前的网络如何,web页面都能像原生应用程序一样顺滑,一样快速响应。

骨架屏的出现。这个想法使得用户更有耐心,因为他们知道正在发生什么,并且在内容实际存在之前能够预测内容,那么他们会认为系统更快。这在很大程度上保持了用户等待的热情。

骨架屏?

这个概念可能包括显示文本,图像或其他内容元。在网上可以看到骨架屏的使用已经非常广泛,Facebook,Google,Slack等公司都在使用。

使用CSS自定义属性实现骨架屏效果

使用CSS自定义属性实现骨架屏效果

举个例子

假设你正在构建一个旅行相关的Web应用程序,用户可以分享他们的旅行以及推荐的地点,它的主要内容可能看起来像这样:

使用CSS自定义属性实现骨架屏效果

您可以将该卡片简化到其基本视觉形状(UI组件的骨架)

使用CSS自定义属性实现骨架屏效果

每当有人从服务器请求新内容时,您可以立即开始显示骨架,同时在后台加载数据。内容准备就绪后,只需将骨架换成实际卡即可。

您可以使用图像来显示骨架,但这会引入额外的请求和数据开销。我们本身已经在这里加载了东西,所以还要去等待另一个图像先加载,这可不是一个好主意。另外图片不是响应式的,如果我们决定调整卡片的样式,我们将不得不更改骨架图像,以便它们再次匹配。?。

一个更好的解决方案是只用 CSS 创建骨架屏。没有额外的请求,最小的开销。而且以后修改更加的方便快捷。

CSS 中绘制骨架

首先,我们需要绘制构成卡片骨架的基本形状。

我们可以通过向background-image属性添加不同的渐变来做到这一点。默认情况下,线性渐变从上到下运行,具有不同的颜色过渡。如果我们只定义一个色标,其余的保持透明,我们就可以绘制形状。

请记住,多个背景图像在这里堆叠在一起,因此顺序很重要。最后一个渐变定义将展示在后面,最先定义的展示在前面。

.skeleton {
  background-repeat: no-repeat;
  background-image: 
    /* layer 2: avatar */
    /* white circle with 16px radius */
    radial-gradient(circle 16px, white 99%, transparent 0),
    /* layer 1: title */
    /* white rectangle with 40px height */
    linear-gradient(white 40px, transparent 0),
    /* layer 0: card bg */
    /* gray rectangle that covers whole element */
    linear-gradient(gray 100%, transparent 0);
}

这些元素通过拉伸来填充整个空间,就像常规的块级元素一样。如果我们想要改变它,我们必须为它们定义明确的尺寸。background-size的值来设置每个图层的宽度和高度,background-size的值的顺序保持我们使用的background-image顺序相同

.skeleton {
  background-size:
    32px 32px,  /* 头像 */
    200px 40px,  /* 标题 */
    100% 100%; /* 卡片背景 */
}

最后一步是将元素定位在卡片上。这与position:absolute类似,跟它的left和top属性的值一样。例如:我们可以给头像和标题 模拟 padding:24px,以匹配真实卡片的外观。

.skeleton {
  background-position:
    24px 24px,  /* 头像 */
    24px 200px, /* 标题 */
    0 0;        /* 卡片背景 */
}

使用自定义属性

如果我们想构建一些稍微复杂一点的东西,CSS 很快就会变得混乱并且很难阅读。如果将代码交给其他开发人员,他们将不知道所有这些神奇数字的来源。维护它肯定会很糟糕。

值得庆幸的是,我们现在可以使用CSS 自定义属性,以更简洁、对开发人员更友好的方式来编写骨架样式。

.skeleton {
  /*
    define as separate properties
  */
  --card-height: 340px;
  --card-padding:24px;
  --card-skeleton: linear-gradient(gray var(--card-height), transparent 0);

  --title-height: 32px;
  --title-width: 200px;
  --title-position: var(--card-padding) 180px;
  --title-skeleton: linear-gradient(white var(--title-height), transparent 0);

  --avatar-size: 32px;
  --avatar-position: var(--card-padding) var(--card-padding);
  --avatar-skeleton: radial-gradient(
    circle calc(var(--avatar-size) / 2), 
    white 99%, 
    transparent 0
  );

  /* 
    now we can break the background up 
    into individual shapes 
  */
  background-image: 
    var(--avatar-skeleton),
    var(--title-skeleton),
    var(--card-skeleton);

  background-size:
    var(--avatar-size),
    var(--title-width) var(--title-height),
    100% 100%;

  background-position:
    var(--avatar-position),
    var(--title-position),
    0 0;
}

这不仅更具可读性,而且以后更改一些值也更容易。另外,我们可以使用一些变量(像 --avatar-size、--card-padding等)来定义实际卡片的样式,并始终使其与骨架版本保持同步。

添加一个媒体查询来调整不同断点的部分骨架现在也很简单:

@media screen and (min-width: 47em) {
  :root {
    --card-padding: 32px;
    --card-height: 360px;
  }
}

浏览器对自定义属性的支持很好,但不是 100%。基本上,所有现代浏览器都支持,IE/Edge 有点晚了。对于这个特定的用例,很容易使用 Sass 变量添加回退。

添加动画

为了使它更好,我们可以为我们的骨架设置动画,让它看起来更像一个加载指示器。我们需要做的就是在顶层放置一个新的渐变,然后用@keyframes.

这是完成骨架卡外观的完整示例:

可以查看预览:https://codepen.io/mxbck/pen/EvmLVp

<div class="card"></div>
/*
 * Variables
 */
:root {  
  --card-padding: 24px;
  --card-height: 340px;
  --card-skeleton: linear-gradient(lightgrey var(--card-height), transparent 0);
  
  --avatar-size: 32px;
  --avatar-position: var(--card-padding) var(--card-padding);
  --avatar-skeleton: radial-gradient(circle 16px at center, white 99%, transparent 0
  );
  
  --title-height: 32px;
  --title-width: 200px;
  --title-position: var(--card-padding) 180px;
  --title-skeleton: linear-gradient(white var(--title-height), transparent 0);
  
  --desc-line-height: 16px;
  --desc-line-skeleton: linear-gradient(white var(--desc-line-height), transparent 0);
  --desc-line-1-width:230px;
  --desc-line-1-position: var(--card-padding) 242px;
  --desc-line-2-width:180px;
  --desc-line-2-position: var(--card-padding) 265px;
  
  --footer-height: 40px;
  --footer-position: 0 calc(var(--card-height) - var(--footer-height));
  --footer-skeleton: linear-gradient(white var(--footer-height), transparent 0);
  
  --blur-width: 200px;
  --blur-size: var(--blur-width) calc(var(--card-height) - var(--footer-height));
}
/*
 * Card Skeleton for Loading
 */
.card {
  width: 280px; //demo
  height: var(--card-height);
  
  &:empty::after {
    content:"";
    display:block;
    width: 100%;
    height: 100%;
    border-radius:6px;
    box-shadow: 0 10px 45px rgba(0,0,0, .1);
    background-image:
      linear-gradient(
        90deg, 
        rgba(lightgrey, 0) 0, 
        rgba(lightgrey, .8) 50%, 
        rgba(lightgrey, 0) 100%
      ),                          //animation blur
      var(--title-skeleton),      //title
      var(--desc-line-skeleton),  //desc1
      var(--desc-line-skeleton),  //desc2
      var(--avatar-skeleton),     //avatar
      var(--footer-skeleton),     //footer bar
      var(--card-skeleton)        //card
    ;
    background-size:
      var(--blur-size),
      var(--title-width) var(--title-height),
      var(--desc-line-1-width) var(--desc-line-height),
      var(--desc-line-2-width) var(--desc-line-height),
      var(--avatar-size) var(--avatar-size),
      100% var(--footer-height),
      100% 100%
    ;
    
    background-position:
      -150% 0,                      //animation
      var(--title-position),        //title
      var(--desc-line-1-position),  //desc1
      var(--desc-line-2-position),  //desc2
      var(--avatar-position),       //avatar
      var(--footer-position),       //footer bar
      0 0                           //card
    ;
    background-repeat: no-repeat;
    animation: loading 1.5s infinite;
  }
}
@keyframes loading {
  to {
    background-position:
      350% 0,        
      var(--title-position),  
      var(--desc-line-1-position),
      var(--desc-line-2-position),
      var(--avatar-position),
      var(--footer-position),
      0 0
    ;
  }
}
/* 
 * Demo Stuff
 */
body {
  min-height:100vh;
  background-color:#FFF;
  display:flex;
  justify-content:center;
  align-items:center;
}

到此这篇关于使用CSS自定义属性实现骨架屏效果的文章就介绍到这了,更多相关css骨架屏内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

 

Tags in this post...

HTML / CSS 相关文章推荐
利用CSS3的transition属性实现滑动效果
Aug 05 HTML / CSS
HTML5地理定位与第三方工具百度地图的应用
Nov 17 HTML / CSS
简单聊聊H5的pushState与replaceState的用法
Apr 03 HTML / CSS
html5 canvas绘制放射性渐变色效果
Jan 04 HTML / CSS
html5的新玩法——语音搜索
Jan 03 HTML / CSS
html5中canvas学习笔记1-画板的尺寸与实际显示尺寸
Jan 06 HTML / CSS
HTML5 Notification(桌面提醒)功能使用实例
Mar 17 HTML / CSS
简单的HTML5初步入门教程
Sep 29 HTML / CSS
H5 meta小结(前端必看篇)
Aug 24 HTML / CSS
利用canvas实现图片压缩的示例代码
Jul 17 HTML / CSS
HTML5 manifest离线缓存的示例代码
Aug 08 HTML / CSS
详解HTML5中CSS外观属性
Sep 10 HTML / CSS
css如何把元素固定在容器底部的四种方式
css中有哪些方式可以隐藏页面元素及区别
Jun 16 #HTML / CSS
CSS控制继承中的height能变为可继承吗
Jun 10 #HTML / CSS
css样式important规则的正确使用方式
Jun 10 #HTML / CSS
分享几个实用的CSS代码块
Jun 10 #HTML / CSS
html中两种获取标签内的值的方法
Jun 10 #HTML / CSS
html5+实现plus.io进行拍照和图片等获取
Jun 01 #HTML / CSS
You might like
PHP图片处理之使用imagecopyresampled函数实现图片缩放例子
2014/11/19 PHP
php使用curl模拟浏览器表单上传文件或者图片的方法
2018/11/10 PHP
zTree插件之多选下拉菜单实例代码
2013/11/06 Javascript
jQuery使用ajax跨域获取数据的简单实例
2016/05/18 Javascript
微信小程序 地图(map)实例详解
2016/11/16 Javascript
JavaScript函数参数的传递方式详解
2017/03/06 Javascript
详解angular2实现ng2-router 路由和嵌套路由
2017/03/24 Javascript
JavaScript+HTML5实现的日期比较功能示例
2017/07/12 Javascript
详解使用Typescript开发node.js项目(简单的环境配置)
2017/10/09 Javascript
node简单实现一个更改头像功能的示例
2017/12/29 Javascript
JS文件中加载jquery.js的实例代码
2018/05/05 jQuery
浅谈js数组splice删除某个元素爬坑
2020/10/14 Javascript
微信小程序实现倒计时功能
2020/11/19 Javascript
使用Typescript开发微信小程序的步骤详解
2021/01/12 Javascript
[01:19:23]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第二场
2018/04/06 DOTA
python插入数据到列表的方法
2015/04/30 Python
Python中datetime常用时间处理方法
2015/06/15 Python
Python实现的排列组合计算操作示例
2017/10/13 Python
Python使用Tkinter实现机器人走迷宫
2018/01/22 Python
python如何重载模块实例解析
2018/01/25 Python
Python使用win32 COM实现Excel的写入与保存功能示例
2018/05/03 Python
Python3视频转字符动画的实例代码
2019/08/29 Python
Python如何使用BeautifulSoup爬取网页信息
2019/11/26 Python
解决os.path.isdir() 判断文件夹却返回false的问题
2019/11/29 Python
Python 实现自动获取种子磁力链接方式
2020/01/16 Python
python新手学习使用库
2020/06/11 Python
奇怪的鱼:Weird Fish
2018/03/18 全球购物
美国鲍勃商店:Bob’s Stores
2018/07/22 全球购物
将"引用"作为函数返回值类型的格式、好处和需要遵守的规则
2016/02/09 面试题
历史学专业毕业生求职信
2013/09/27 职场文书
历史学专业大学生找工作的自我评价
2013/10/16 职场文书
2014年公务员思想汇报范文:全心全意为人民服务
2014/03/06 职场文书
环境保护建议书
2014/08/26 职场文书
工作经常出错的检讨书
2014/09/13 职场文书
归元寺导游词
2015/02/06 职场文书
MySQL表的增删改查(基础)
2021/04/05 MySQL