likes
comments
collection
share

实现一个vue3组件库 - skeleton骨架屏

作者站长头像
站长
· 阅读数 17

实现一个vue3组件库 - skeleton骨架屏 pid: 109441068

前言

骨架屏的实现思路大致有三种:

  • 手动维护骨架屏的代码
  • 使用图片作为骨架屏
  • 自动生成骨架屏

这里的骨架屏指第一种。最终实现效果移步:Skeleton 骨架屏 | SSS UI Plus

骨架屏不会涉及很多逻辑代码,倒是css很有意思。

实现

骨架屏分为skeletonskeletonItem两个组件。所有骨架屏Item都应该是一个普通的矩形盒子, 通过css样式来改变外观和布局。

skeleton

最外层skeleton应该具有两个槽,一个作为真实UI, 一个作为骨架屏。且需要一个props属性来控制显示两者其一(props:loading).

code链接: sss-ui-plus/packages/SSkeleton

如何控制skeletonItem组件只能作用于skeleton组件内部呢? 通过vue3提供的provide inject, 在skeleton中provide某个变量, 在skeletonItem中inject这个变量,当skeletonItem没有接收到此变量时,抛出异常即可。

provide('hasSkeletonAnimation', props.animated)
provide('isWithinSkeleton', true)

如何控制在skeleton slot 和 真实UI slots之间切换时延时?

通过内部_loading属性控制显示哪一个slot, _loading属性受控于props.loading属性, 当props.loading属性切换为true时,延时一段时间后再设置_laoding变为true即可。

let timer: NodeJS.Timeout;

const _loading = ref<Boolean>(props.loading);

watch(() => props.loading, () => {
    clearTimeout(timer);    //注意要清除定时器

    if (props.loading) {
       _loading.value = true;
    } else {
       timer = setTimeout(() => {
          _loading.value = false;
       }, props.throttle);
    }
})

skeletonItem(主要)

code链接: sss-ui-plus/packages/SSkeletonItem

首先是skeletonItem的结构:

<template>
    <div
       ref="item"
       class="sss-skeleton-item"
       :class="[{
          'is-circle':props.isCircle,
          'is-round':props.isRound,
          'animated':animated,
       },
       `sss-skeleton-item-image`
       ]"
    >
       <svg v-if="props.type === 'image'"  viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
          <path fill="currentColor"
                d="M96 896a32 32 0 0 1-32-32V160a32 32 0 0 1 32-32h832a32 32 0 0 1 32 32v704a32 32 0 0 1-32 32H96zm315.52-228.48-68.928-68.928a32 32 0 0 0-45.248 0L128 768.064h778.688l-242.112-290.56a32 32 0 0 0-49.216 0L458.752 665.408a32 32 0 0 1-47.232 2.112zM256 384a96 96 0 1 0 192.064-.064A96 96 0 0 0 256 384z"></path>
       </svg>
    </div>
</template>

这里如果类型是image,采用了element plus的一个svg图标

部分样式文件:

.sss-skeleton-item{
    //省略了部分样式

    &.animated {
        //使用扫描线
        .useBGScanningLine(var(--sss-color-gray-deep-fade));
    }
}

扫描线动画

采用的是less的混合函数

.useBGScanningLine (@bgColor){
    background: linear-gradient(
            100deg,
            rgba(255, 255, 255, 0) 30%,
            rgba(255, 255, 255, .5) 50%,
            rgba(255, 255, 255, 0) 70%
    ) @bgColor;
    background-size: 200% 100%;
    background-position-x: 180%;
    animation: 1.2s __bgLine linear infinite;
}
@keyframes __bgLine {
    to {
        background-position-x: -20%;
    }
}

现在我们一项项分析

首先准备一个demo:

<template>
    <div class="demo"></div>
</template>

<style lang="less">
@import "src/styles/global";
.demo{
    height: 40px;
    width: 600px;
    .useBorderGray();
    .useBorderRadius7px();
}
</style>

实现一个vue3组件库 - skeleton骨架屏

background属性使用了linear-gradient线性渐变时:

background: linear-gradient(
    red, silver //由于是线性渐变,所以至少要两个颜色
);

可以观察到默认是向下渐变 实现一个vue3组件库 - skeleton骨架屏

现在我们设置渐变方向,并且设置不同颜色的占比。

background: linear-gradient(
    100deg,  //表示以数学中的坐标系的y负轴为参考,逆时针转100度
    red 40%,
    silver 50%,
    red 60%
);

可以观察到这时在40%-60%之间出现了一条线 实现一个vue3组件库 - skeleton骨架屏

当背景颜色不存在(透明)时,显示背景基色

在上面代码中我们都没设置颜色的透明度和背景基色,代码修改如下

background: linear-gradient(
    100deg,
    rgba(255,0,0,0.5) 40%,
    silver 50%,
    rgba(255,0,0,0.5) 60%
) black;

可以观察到红色背景透明时会显示出背景基色(黑色); 实现一个vue3组件库 - skeleton骨架屏

background-position-x 如其名,用于设置背景的x偏移量:

height: 40px;
width: 600px;
.useBorderGray();
.useBorderRadius7px();

background: linear-gradient(
    100deg,
    rgba(255,0,0,0.5) 40%,
    silver 50%,
    rgba(255,0,0,0.5) 60%
) black;
background-position-x: 180%;

你会观察到背景没有任何变化,为什么? 实现一个vue3组件库 - skeleton骨架屏

因为背景默认会占满整个盒子,也就是背景的大小 === 盒子大小,此时你设置偏移量不会起作用。当我们放大背景时,偏移量就起作用了。

height: 40px;
width: 600px;
.useBorderGray();
.useBorderRadius7px();

background: linear-gradient(
    100deg,
    rgba(255,0,0,0.5) 40%,
    silver 50%,
    rgba(255,0,0,0.5) 60%
) black;
background-size: 200% 100%;

background-position-x: 10%; 实现一个vue3组件库 - skeleton骨架屏 background-position-x: 20%; 实现一个vue3组件库 - skeleton骨架屏 background-position-x: 30%; 实现一个vue3组件库 - skeleton骨架屏

此时你会发现,偏白色区域会随着background-position-x属性而变化!

此时我们给他运用一个动画就行实现扫描线的效果了!

.demo{
    height: 40px;
    width: 600px;
    .useBorderGray();
    .useBorderRadius7px();

    background: linear-gradient(
       100deg,
       rgba(255,0,0,0.5) 40%,
       silver 50%,
       rgba(255,0,0,0.5) 60%
    ) black;
    background-size: 200% 100%;
    background-position-x: 180%;
    animation: 1.2s __bgLine linear infinite;


}
@keyframes __bgLine {
    to {
       background-position-x: -20%;
    }
}

需要注意的是,background-position-x需要根据需要进行微调.

最后把它封装为一个less混合函数:

.useBGScanningLine (@bgColor){
    background: linear-gradient(
            100deg,
            rgba(255, 255, 255, 0) 30%,
            rgba(255, 255, 255, .5) 50%,
            rgba(255, 255, 255, 0) 70%
    ) @bgColor;
    background-size: 200% 100%;
    background-position-x: 180%;
    animation: 1.2s __bgLine linear infinite;

}

写在最后

这个组件也许有很多不完善的地方,欢迎指出!

这个项目的地址是:lastertd/sss-ui-plus: 适用于vue3的组件库 (github.com)在这里求一个star✨

感谢看到最后💟💟💟

转载自:https://juejin.cn/post/7267948449966276671
评论
请登录