使用CSS自定义属性构建骨架屏

写在前面

几天前看到薄荷前端团队分享的《前端骨架屏方案小结》,忽然回想起一年前看到的max bock写的《Building Skeleton Screens with CSS Custom Properties》,翻译整理写下出此文,分享一下使用CSS自定义属性构建骨架屏的技巧,先看骨架屏demo效果吧css


设计Web上的加载状态经常被忽略或被认为是过后考虑。性能不只是前端开发人员的职责,构建与慢速链接一块儿工做的体验也是设计挑战。 虽然前端开发人员须要注意一些事情,好比压缩和缓存,可是设计人员必须考虑UI处于“加载”或“离线”状态时的外观和行为。前端


速度幻觉

随着咱们对移动体验的指望发生变化,咱们对性能的理解也在变化。咱们指望网络应用程序感受像本机应用程序同样快速响应,不管其当前的网络覆盖范围如何。

感知性能是衡量用户感受速度的尺度。这个想法是用户更有耐心,而且若是他们知道正在发生什么,而且在内容实际存在以前可以预测内容,那么他们会认为系统更快。这在很大程度上与管理指望和保持用户知情有关。
git

对于Web应用程序,这个概念可能包括显示文本,图像或其余内容元素的“模型” - 称为
骨架屏💀。能够在网上能够看到,Facebook,Google,Slack等公司使用:


(Facebook的骨架屏)
github


(Slack的骨架屏)浏览器

例子

假设你正在构建一个Web应用程序,这是一种旅行建议类型的东西,人们能够分享他们的旅行和推荐地点,因此你的主要内容可能看起来像这样:


您能够将该卡片缩小到其基本视觉形状(UI组件的骨架)


每当有人从服务器请求新内容时,您能够当即开始显示骨架,同时在后台加载数据。内容准备就绪后,只需将骨架换成实际卡便可。这可使用普通的JavaScript或使用像Vue/React这样的库来完成。

如今咱们可使用图像来显示骨架,但这会引入额外的请求和数据开销。咱们已经在这里加载了东西,因此等待另外一个图像首先加载并非一个好方式。此外,它没有响应,若是咱们决定调整一些内容卡的样式,咱们将不得不复制骨架图像的更改,以便它们再次匹配。😒

一个更好的解决方案是只用CSS建立整个东西。没有额外的请求,最小的开销,甚至没有任何额外的标记。咱们能够用下面的方式来构建它,使之后更改设计变得更容易。

经过CSS绘制骨架

首先,咱们须要绘制构成卡片骨架的基本形状。咱们能够经过background-image属性添加不一样的渐变来实现这一点。默认状况下,线性渐变从上到下运行,有不一样的颜色中止过渡。若是咱们只定义一个颜色中止,并使其他颜色保持透明,咱们能够绘制形状。

请记住,在这块,多个背景图像堆叠在一块儿,所以顺序很是重要。最后一个渐变定义在后面,第一个位于前面。

.skeleton {
  background-repeat: no-repeat;
  background-image:
    radial-gradient(circle 16px, white 99%, transparent 0), /* 第3层 头像 */
    linear-gradient(white 40px, transparent 0), /* 第2层 标题 */
    linear-gradient(gray 100%, transparent 0); /* 第1层 卡片背景 */
}
复制代码

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

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

最后一步是将元素放在卡片上。这与position:absolute相似,表示left和top属性的值同样。例如,例如:咱们能够给头像和标题 模拟24px的填充,以匹配真实内容卡的外观。
缓存

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


使用自定义属性将其分解

这在一个简单的例子中效果很好, 可是若是咱们想要构建一些稍微复杂的东西,那么CSS会很快变得混乱而且很难阅读。若是代码交接给另一个前端开发人员,他们就不知道全部这些神奇的数字来是从哪里来的,显然这是不易难维护的。

因而乎,这里提出用自定义CSS属性,以更加简洁,更有利于前端开发人员的方式编写骨架样式 ,甚至能够考虑不一样值之间的关系:

.skeleton {
  /*
    定义单独的属性
  */
  --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
  );

  /* 
    如今咱们能够把背景分解成单独的形状
  */
  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;
}
复制代码

这不只可读性更好,并且之后更改一些值也更容易。另外,咱们还可使用一些变量(好比头像大小、卡片填充)来定义实际卡片的样式,并始终使其与骨架版本保持同步。添加一个媒体查询来调整不一样断点的骨架部分如今也很是简单:bash

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

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

添加动画

为了使这更好,咱们能够为咱们的骨架设置动画,并使其看起来更像是一个加载指示器。
咱们须要作的就是在顶层放置一个新的渐变,而后使用它来设置其位置的动画 @keyframes
如下是成品骨架卡外观的完整 骨架屏-demo


固然你可使用:empty选择器和伪元素来绘制骨架,所以它只适用于空卡片元素,一旦注入了内容,框架屏幕就会自动消失
网络

最后,感兴趣的同窗可去我github下载这个骨架屏-demo源码传送门框架