骨骼猛mm-或我如何解决可重复使用的骨骼装载机的问题。
#javascript #css #ui #skeleton

介绍。

互联网上有很多很棒的文章,这些文章专门针对骨架装载机,涵盖了它们使用的类型,情况和需求。我不会在这里列出它们,您可以在您喜欢的搜索引擎中轻松找到它们。

详细调查了此主题后,我决定自己创建一个非常简单,灵活,可重复使用,可定制和轻巧的解决方案,适合大多数用例。
在本文中,我将描述创建该解决方案并将其转变为库的过程,以及我在工作时遇到的困难。

什么是骨架装载机?

注意:,如果您知道哪些骨架装载器是。

,可以跳过此部分。

骨架加载程序,也称为骨骼屏幕或内容占位符,是一种用户界面设计模式,用于在Web和移动应用程序中的内容加载过程中增强用户体验。当数据在后台获取或处理时,而不是显示空白或空屏幕,而是通过为用户提供可视提示的期望,减少感知到的加载时间并减轻潜在的挫败感,而不是显示骨架加载程序。

这是LinkedIn和YouTube的骨架加载器的示例:

LinkedIn skeleton screen

YouTube skeleton screen

为什么必须使用骨架装载机?

  • 改善用户体验:骨架加载器通过提供视觉反馈并减少内容加载延迟的感知来增强用户体验。
  • 降低跳出率:骨架加载器可以防止用户由于加载延迟而离开页面。
  • 平稳的过渡:它们在页面或应用程序的不同状态之间创建更平滑的过渡。
  • 与旋转器不同,骨架装载机吸引了用户对进步而不是等待时间的关注。

大多数现有骨骼的问题。

考虑到创建自己的骨架加载程序或库为您做的很多示例,它们仍然存在许多问题。

  • 有限的自定义:许多现有的骨架具有有限的自定义选项。它导致实际内容设计和骨架样式的不匹配。
  • 尽管他们的目的是提供视觉表示,但其中大多数并不适合视觉障碍的用户或使用屏幕读取器的人。
  • 多功能性和可重复性。创建骨骼的大多数方法都提供占位符的浅层副本,从而产生许多类似的副本,或者基本上改变了现有组件的结构。两种方法都需要许多其他代码和资产。
  • 维护复杂性:随着网站的发展和内容的变化,保持骨架装载机的最新状态可能成为维护负担。

骨架装载机替代方案。

使用骨骼有几种“替代”。展望未来并回答是否确实有替代方案,我的答案不是而是是。如果我们谈论正确的用法,那么骨骼是最好的解决方案之一。下面,我仍然会提供一些替代方案,以及他们的利弊。

旋转器

旋转器,是骨架装载机的常见替代品。它们由连续旋转的动画图标组成,提供了内容正在加载的视觉提示。

专业人士

  • 简单性:简单的实现,通常只需要几行代码或使用预先设计的库。
  • 普遍理解:旋转器在不同平台和应用程序中得到广泛认可,以确保用户了解内容正在加载。

cons。

  • 有限的信息:旋转器不提供有关已加载内容的任何上下文。
  • 与整个页面或大部分页面重叠,而不是单个元素。是什么使加载的感觉不是单个元素,而是整个网站的整体。

旋转器是接口的组成部分,但它们并不完全适合更换骨骼。

进度栏

进度栏是指示任务或过程的完成状态的视觉元素。它提供了线性表示,通常具有逐渐生长的填充部分。

专业人士

  • 精确的反馈:提供有关任务完成状态的准确而精确的反馈。
  • 时间估计:进度条可以为用户估算完成所需的剩余时间。
  • 多功能:进度条可以在各种情况和场景中使用,使其成为网络和应用程序开发中的多功能组件。

cons。

  • 缺乏上下文:在某些情况下,进度条可能无法提供有关其代表的实际任务或过程的充分背景。
  • 实施复杂性:创建具有准确表示和平滑动画的进度条可能很复杂,尤其是在处理不同的任务持续时间和响应能力时。

进度条更适合显示文件上传或定量进度进度的方案。它们通常在页面顶部使用,以显示整个页面加载的进度。但是它们不能作为骨骼的等效替代品,因为它们是用于其他目的的。

没有任何视觉。

是的,没有任何装载机或占位符也是另一种选择。在某些情况下,这将是一个更好的解决方案,而不是使用不合适的元素。
主要是唯一的优点是您不需要在实施上花费的额外时间和资源。但是,这是显而易见的缺点 - 对您的网站的设计不太吸引人,并且对加载时间较慢的看法。

创建多功能和可重复使用的骨架加载程序。

我对骨骼有足够的了解,何时使用骨骼,是什么以及开发的方法,我试图自己确定我的最终结果应该是什么。

多功能且可重复使用。
Internet上有很多示例,具有过度复杂的方法,您必须为要使用它们的每个组件创建一个单独的骨架。就我而言,我希望它可以在大多数情况下可以重复使用,而不是坚持任何JavaScript框架(例如React.js或vue.js)。

配置灵活性。
由于每个项目和每个情况都可能大不相同,因此我的骨架需要能够配置。

功能丰富。
除了标准功能集外,我还想用支持其他有用和必要功能的支持。

轻巧和不依赖性。
轻巧,尽可能免费。

所有这些期望和调查都使我了解到,我的未来骨骼必须用纯CSS写成,而没有任何JavaScript和第三方依赖性。这使得可以轻巧,并且依赖依赖。主要思想是它继承了应用于应用的组件的布局,并使用自己的样式自定义它们。
也许将来出于开发目的,将所有这些都重写为SCSS语法更有意义,因为这将使代码更短,更可重复使用,并且最终组装仍将编译为纯CSS。

基本卡。

作为一个基础示例,出于演示目的,我将使用React.js并进行一些基本卡片标记来展示其工作原理。但是我提醒您,它没有与任何框架绑定,并且在文章的末尾将有图书馆和演示的源代码的链接。

这是一个卡片示例,具有自己的样式,并且还不知道骨骼的存在。

<div className='card'>
    <div className='card__img-wrapper'>
        <img className='card__img' src={require(`../../images/cards/${imgUrl}`)}/>
    </div>
    <div className='card__body'>
        <div className='card__details'>
            <p className='card__title'>{title}</p>
            <p className='card__subtitle'>{subtitle}</p>
        </div>
    </div>
</div>

为了使骨架变得活跃,只需要将父级sm-loading应用于卡本身,将sm-item-primarysm-item-secondary应用于我们希望看到骨骼的元素。因此,更新的结果将看起来像这样:

<div className={`card ${dataState.dataStatus === 'loading' ? "sm-loading" : ""}`}>
    <div className='card__img-wrapper sm-item-primary'>
        <img className='card__img' src={require(`../../images/cards/${imgUrl}`)}/>
    </div>
        <div className='card__body'>
            <div className='card__details'>
                <p className='card__title sm-item-secondary'>{title}</p>
                <p className='card__subtitle sm-item-secondary'>{subtitle}</p>
            </div>
    </div>
</div>

让我分解并解释一些片刻:
在以下代码

<div className={`card ${dataState.dataStatus === 'loading' ? "sm-loading" : ""}`}>

我根据条件使用sm-loading类。如果dataState.dataStatus的状态为loading,则将应用类,否则 - 不。 sm-loading类仅在加载数据时设置/存在。这是一种切换器。只有在存在时,具有适当类sm-item-primarysm-item-secondary的儿童元素才会显示骨骼。因此,只有3堂课可以使该骨架起作用。

基本骨骼样式。

根变量。

为了具有整洁和可重复使用的代码,以及进一步配置的可能性(覆盖),我创建了具有基本样式的根变量。

/* Root variables.
--------------------------------------------------------------------------------*/
:root {
    /* Light theme colors. */
    --sm-color-light-primary: 204, 204, 204, 1;
    --sm-color-light-secondary: 227, 227, 227, 1;
    --sm-color-light-animation-primary: color-mix(
            in srgb,
            #fff 15%,
            rgba(var(--sm-color-light-primary))
    );
    --sm-color-light-animation-secondary: color-mix(
            in srgb,
            #fff 15%,
            rgba(var(--sm-color-light-secondary))
    );

    /* Dark theme colors. */
    --sm-color-dark-primary: 37, 37, 37, 1;
    --sm-color-dark-secondary: 41, 41, 41, 1;
    --sm-color-dark-animation-primary: color-mix(
            in srgb,
            #fff 2%,
            rgba(var(--sm-color-dark-primary))
    );
    --sm-color-dark-animation-secondary: color-mix(
            in srgb,
            #fff 2%,
            rgba(var(--sm-color-dark-secondary))
    );

    /* Animations. */
    --sm-animation-duration: 1.5s;
    --sm-animation-timing-function: linear;
    --sm-animation-iteration-count: infinite;
}

在这里设置了静态的颜色值(没有动画)和动画骨骼以及动画设置。

基本样式。

文件的下一部分是针对基本样式的,与任何配色方案或配置无关。

/* Base styles.
Applied by default and not related to any of the color scheme.
--------------------------------------------------------------------------------*/
.sm-loading .sm-item-primary,
.sm-loading .sm-item-secondary {
    border-color: transparent !important;
    color: transparent !important;
    cursor: wait;
    outline: none;
    position: relative;
    user-select: none;
}

.sm-loading .sm-item-primary:before,
.sm-loading .sm-item-secondary:before {
    clip: rect(1px, 1px, 1px, 1px);
    content: "Loading, please wait.";
    inset: 0;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
}

.sm-loading .sm-item-primary::placeholder,
.sm-loading .sm-item-secondary::placeholder {
    color: transparent !important;
}

.sm-loading .sm-item-primary *,
.sm-loading .sm-item-secondary * {
    visibility: hidden;
}

.sm-loading .sm-item-primary :empty:after,
.sm-loading .sm-item-primary:empty:after,
.sm-loading .sm-item-secondary :empty:after,
.sm-loading .sm-item-secondary:empty:after {
    content: "\00a0";
}

/* Animations related styles. */
@keyframes --sm--animation-wave {
    to {
        background-position-x: -200%;
    }
}

@keyframes --sm--animation-wave-reverse {
    to {
        background-position-x: 200%;
    }
}

@keyframes --sm--animation-pulse {
    0% {
        opacity: 1;
    }
    50% {
        opacity: 0.6;
    }
    100% {
        opacity: 1;
    }
}

.sm-loading .sm-item-primary,
.sm-loading .sm-item-secondary {
    animation: var(--sm-animation-duration) --sm--animation-wave
    var(--sm-animation-timing-function) var(--sm-animation-iteration-count);
}

如前所述,父级sm-loading用于激活骨架加载器样式。 sm-item-primarysm-item-secondary类覆盖了元素样式并显示骨架。

Skeleton Mammoth items structure

以这种方式,骨架加载程序持续并继承了元素的布局样式和尺寸(在我们的情况下)。此外,我想说的是,使用这种方法,我们保证sm-item-primarysm-item-secondary类的所有子元素都被隐藏,并且至少具有Non-breaking space字符。如果元素根本不包含内容,则此符号可确保显示和渲染元素。还有一个部分负责屏幕阅读器的用户,并让他们知道内容正在加载过程中。

此外,主题部分有一个分区,例如配色方案,动画,可访问性。让我们看一下光主题的颜色样式。

/* Light theme.
The library's default color scheme.
Styles applied to the light color scheme.
--------------------------------------------------------------------------------*/
.sm-loading .sm-item-primary {
    background: rgba(var(--sm-color-light-primary));
}

.sm-loading .sm-item-secondary {
    background: rgba(var(--sm-color-light-secondary));
}

/* Animations related styles. */
.sm-loading .sm-item-primary {
    background: linear-gradient(
            90deg,
            transparent 40%,
            var(--sm-color-light-animation-primary) 50%,
            transparent 60%
    )
    rgba(var(--sm-color-light-primary));
    background-size: 200% 100%;
}

.sm-loading .sm-item-secondary {
    background: linear-gradient(
            90deg,
            transparent 40%,
            var(--sm-color-light-animation-secondary) 50%,
            transparent 60%
    )
    rgba(var(--sm-color-light-secondary));
    background-size: 200% 100%;
}

配色方案。

借助CSS媒体功能prefers-color-scheme,我已经实现了对浅色和黑暗主题的自动支持。根据用户的设置,它将自动应用。当然,可以手动设置它,我将在文章稍后讨论。

/* Dark theme.
Styles to apply if a user's device settings are set to use dark color scheme.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
--------------------------------------------------------------------------------*/
@media (prefers-color-scheme: dark) {
    /*Omitted pieces of code.*/
}

可访问性。

动画。

默认情况下,在骨架中,我决定启用动画,但是在某些情况下,开发人员或用户宁愿不拥有它。如果对于前者,这可能取决于设计和要求,那么对于后者,可能是由于前庭运动障碍。
为此,CSS媒体功能prefers-reduced-motion进行了营救。

/* Accessibility.
Disable animations if a user's device settings are set to reduced motion.
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
--------------------------------------------------------------------------------*/
@media (prefers-reduced-motion) {
    /*Omitted pieces of code.*/

    .sm-loading .sm-item-primary,
    .sm-loading .sm-item-secondary {
        animation: none;
    }

    /*Omitted pieces of code.*/
}

配置。

在此阶段,主要样式已经结束,可以将骨骼视为准备就绪。但是,我想到我应该能够配置上述所有内容的想法使我感到困扰。如果我想关闭动画,如果我想始终有一个黑暗的主题怎么办?
由于CSS无法像JavaScript函数一样接收任何值,因此排除JavaScript的添加(至少在此阶段)。因为这将完全打破尽可能简单和轻量级的主要概念。
但是,如果我们事先知道它们的价值观,我们仍然可以实施类似于参数的东西。在这里,data-*属性得到了我们的帮助。在他们的帮助下,我们可以检查属性中所需的价值的存在,并应用所需的样式。
我将向您展示如何在一小部分代码上实现它,您可以在文章末尾的链接中的源代码中找到完整的实现。

例如,如果要明确使用黑暗主题,则需要制作一个JSON对象:

const config = JSON.stringify({
  theme: "dark",
})

注意:
数据 - *属性只能与字符串一起使用,因此将JSON.stringify()方法应用于配置对象很重要。

接下来,将此对象传递到自定义属性data-sm-config

<div class="card sm-loading" data-sm-config={config}>
    <!-- Omitted pieces of code. -->
</div>

这就是它在CSS文件中的外观。如果data-sm-config中有一个值"theme":"dark",请应用所需的样式。

.sm-loading[data-sm-config*='"theme":"dark"'] .sm-item-primary,
.sm-loading[data-sm-config*='"theme":"dark"'] .sm-item-secondary {
    /* Omitted pieces of code. */
}

高级用法。

具有全球变量的覆盖样式。

每个项目和案例都是独特的,不可能预测和使一切通用。特别是在颜色方面。这就是为什么在文章开头所说的那样,大多数值都放在变量中。如果要调整默认样式,只需在:root CSS伪级中覆盖自己的*.css文件中的适当变量。
因此,例如,如果要更改主要项目的颜色(使用类sm-item-primary),则只需要覆盖相应的变量:

/* Your own custom.css file: */
:root {
  --sm-color-light-primary: 255, 0, 0, 0.5;
}

现场演示。

Skeleton Mammoth live demo animation

您可以在以下链接上尝试完成的结果:Live demo

让我们把它结合起来。

我很长一段时间以来研究了骨架装载机的主题,它们的品种,用法,开发方法,我设法收集了有用信息的本质并将其转变为最终产品。在收集了最佳实践,改进它们并将其合并为一个实体之后,我创建了名为Skeleton Mammoth的库。我相信我设法实现了自己的目标,并创建了一个很好的图书馆,并在本文中描述了所有优点。我希望这个图书馆在使用时能够使人们受益,或者提供新的知识和经验来创建自己的东西。

表示您的支持。

如果您觉得我的库有用并且想显示您的支持,则有一些简单的方法:
明星GitHub存储库这有助于提高其可见性,并让其他人知道库具有强大的用户群。

传播单词您可以通过在任何平台上共享有关它的信息将新用户介绍给库。例如在博客文章上写下有关它的文章,在社交媒体上提及它或在相关开发人员社区中进行讨论,这将非常有帮助。

下面,我将发布一个有用链接的列表,包括库的链接。

有用的链接。