如何实现一个骨架屏
总结骨架屏的思路:
- 比如一种场景,数据请求不及时,就显示骨架屏的内容。
- 骨架屏的本质就是一个div或者是其他标签,设置背景色和动画,让他进行位移,让用户产生“数据还在加载中”的这个印象
html+css实现
我们先用一个html文件+css来模拟实现,然后再去vue里面创建组件
第一步,创建div盒子结构下
<div class="father">
123
<div class="box">
</div>
</div>
第二步,设置好基本的样式
.father {
width: 200px;
height: 200px;
background-color: gray;
}
.box {
width: 200px;
height: 200px;
}
浏览器效果如图所示:
第三步,我们给孩子添加如下属性:
.box {
width: 200px;
height: 200px;
background: linear-gradient(to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%);
}
如果你记不住background: linear-gradient是干嘛的,请参考我的这篇文章,
第四步,我们定义一个动画
@keyframes shan {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
第五步,我们使用这个动画
.box {
+ animation: shan 1.5s ease 0s infinite;
width: 200px;
height: 200px;
background: linear-gradient(to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%);
}
到这一步,我们的动画还是没有反应,这是为什么呢?我们下一步,设置了就有了
第六步,我们设置子绝父相
.father {
+ position: relative;
+ width: 200px;
height: 200px;
background-color: gray;
}
.box {
+ position: absolute;
top: 0;
width: 200px;
height: 200px;
animation: shan 1.5s ease 0s infinite;
background: linear-gradient(to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%);
}
设置完成后,我们发现动画成功了。
但是,为什么设置了定位以后,动画才能够生效呢?小编 也不知道,能不能帮俺分析一下呢?我感觉还是没能掌握 骨架屏的精髓
第七步,我们设置一个属性,让他进行倾斜,参数1是在x轴上面倾斜,参数2是在y轴上倾斜
.box {
position: absolute;
top: 0;
width: 200px;
height: 200px;
animation: shan 1.5s ease 0s infinite;
background-color: pink;
background: linear-gradient(to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%);
+ transform: skew(-30deg);
}
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
position: relative;
width: 200px;
height: 200px;
background-color: gray;
}
.box {
position: absolute;
top: 0;
width: 200px;
height: 200px;
animation: shan 1.5s ease 0s infinite;
background-color: pink;
background: linear-gradient(to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%);
transform: skew(-30deg);
}
@keyframes shan {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
</style>
</head>
<body>
<div class="father">
123
<div class="box">
</div>
</div>
</body>
</html>
vue组件封装的实现
第一步,template模板,
- 暴露width属性 和 height属性,骨架屏的宽高会变化
- 暴露animate属性,用于“是否开启动画shan”
- 暴露bg属性,用于父盒子的背景颜色
<div
class="xtx-skeleton"
:style="{ width: width, height:height }"
:class="{ shan: animated }"
>
<!-- 1 盒子-->
<div class="block" :style="{ backgroundColor: bg }"></div>
<!-- 2 闪效果 xtx-skeleton 伪元素 --->
</div>
第二步,script部分
<script>
export default {
name: 'XtxSkeleton',
// 使用的时候需要动态设置 高度,宽度,背景颜色,是否闪下
props: {
bg: {
type: String,
default: '#efefef'
},
width: {
type: String,
default: '100px'
},
height: {
type: String,
default: '100px'
},
animated: {
type: Boolean,
default: false
}
}
}
</script>
第三步,样式部分
和之前的类似,只是,这里用after伪元素代替之前的子盒子
<style scoped lang="less">
.xtx-skeleton {
display: inline-block;
position: relative;
overflow: hidden;
vertical-align: middle;
.block {
width: 100%;
height: 100%;
border-radius: 2px;
}
}
.shan {
&::after {
content: "";
position: absolute;
animation: shan 1.5s ease 0s infinite;
top: 0;
width: 50%;
height: 100%;
background: linear-gradient(
to left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.3) 50%,
rgba(255, 255, 255, 0) 100%
);
transform: skewX(-45deg);
}
}
@keyframes shan {
0% {
left: -100%;
}
100% {
left: 120%;
}
}
</style>
使用这个组件 假设这里有一个组件使用,v-if判断,一定是有这个goods数据,才会渲染组件,没有数据,ul不会渲染,就显示skeleton骨架屏组件
+ <ul v-if="goods && goods.length" class="goods-list">
<li v-for="item in goods" :key="item.id">
<RouterLink :to="`/product/${item.id}`">
<img v-lazy="item.picture" alt="">
<p class="name ellipsis">{{item.title}}</p>
<p class="price">¥100</p>
</RouterLink>
</li>
</ul>
+ <home-skeleton v-else bg="#f0f9f4"/>