vue 骨架屏组件

14 篇文章 0 订阅
2 篇文章 0 订阅

注意:如果同学们不使用page-skeleton-webpack-plugin和vue-server-renderer,并且,你希望生成的骨架屏可以和页面相匹配,可以往下看!!!

 

 

1、实现原理:

给需要的dom标签设置特定类名,使用getBoundingClientRect来获取dom标签的大小和位置信息,并以此来生成一个占位符,最终生成一份“骨架屏”,当页面加载完毕,移除该“骨架屏”组件即可。因为这是在mounted周期获取元素信息,故如果元素信息不满意,需要先默认一些初始数据

2、代码如下:

   (1) index.vue:

<template>
    <div  class='skeleton' :style="skeletonStyle">
        <div v-for="(item,index) in darks"
             class="item dark"
             :style="{ ...createStyle(item), backgroundColor: darkColor }"
             :key="'darks'+index">
        </div>
        <div v-for="(item,index) in lights"
             class="item light"
             :style="{ ...createStyle(item), backgroundColor: lightColor }"
             :key="'lights'+index">
        </div>
        <div v-for="(item,index) in squares"
             class="item square"
             :style="createStyle(item)"
             :key="'squares'+index">
        </div>
        <div v-for="(item,index) in circulars"
             class="item circular"
             :style="createStyle(item)"
             :key="'circulars'+index">
        </div>
        <div v-for="(item,index) in cylinders"
             class="item cylinder"
             :style="createCylinderStyle(item)"
             :key="'circulars'+index">
        </div>
    </div>
</template>

<script>
    // 约定.skeleton样式类为骨架屏查找绘制节点的根节点
    // 约定.skeleton-square 样式类,表示绘制当前节点的骨架节点样式为方形(如商品卡片)
    // 约定.skeleton-circular样式类,表示绘制当前节点的骨架节点为圆形(如logo)
    // 约定.skeleton-cylinder样式类,表示绘制当前节点的骨架节点为长条形(如搜索框)
    // 约定.skeleton-light与.skeleton-dark为块元素背景骨架样式
    export default {
        name: "Skeleton",
        data(){
            return{
                lights:[],
                darks: [],
                squares: [],
                circulars: [],
                cylinders: [],
            }
        },
        props:{
            selector: {
                 type:String,
                 required:false,
                 default:'skeleton'
            },
            backgroundColor: {
                type:String,
                required:false,
                default:'#fff'
            },
            lightColor: {
                type:String,
                required:false,
                default:'white'
            },
            darkColor: {
                type:String,
                required:false,
                default:'#2f3333'
            },
            top: {
                type:String,
                required:false,
                default:'0'
            },
        },
        mounted() {
            Promise.all([
                this.selectAll(`.${this.selector}-light`),
                this.selectAll(`.${this.selector}-dark`),
                this.selectAll(`.${this.selector}-square`),
                this.selectAll(`.${this.selector}-circular`),
                this.selectAll(`.${this.selector}-cylinder`),
            ]).then(([lights, darks, squares, circulars, cylinders]) =>{
                this.lights=lights
                this.darks=darks
                this.squares=squares
                this.circulars=circulars
                this.cylinders=cylinders
                }
            )
        },
        computed:{
            skeletonStyle(){
                return {
                    backgroundColor:this.backgroundColor,
                    top:this.top
                }
            }
        },
        methods: {
            selectAll(selector) {
                return new Promise(resolve =>{
                    let domList = document.querySelectorAll(selector)
                    let resultList = []
                    for(let a=0;a<domList.length;a++){
                        resultList.push(domList[a].getBoundingClientRect())
                    }
                    console.log('domList',domList)
                    resolve(resultList)
                  }
                )
            },
            createStyle({ width, height, top, left }){
               return {
                   width: `${width}px`,
                   height: `${height}px`,
                   top: `${top}px`,
                   left: `${left}px`,
               }
            },
            createCylinderStyle(rect){
                return {
                    ...this.createStyle(rect),
                    'border-radius': `${rect.height / 2}px`,
                }
            }
        }
    }
</script>

<style scoped lang="scss">
@import "./index";
</style>

(2)index.scss

$max-index: 1000;
$skeleton-index: $max-index - 3;
$skeleton-item-index: $skeleton-index + 2;
$skeleton-container-index: $skeleton-index + 1;
$grey-background-color: #f5f5f5;
$border-radius: 8px;
.skeleton {
  position: fixed;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  height: 100vh;
  z-index: 9997;
  overflow: hidden;
  .item {
    position: fixed;
    display: inline-block;
    background: $grey-background-color;
    z-index: $skeleton-item-index;

    &.dark,
    &.light {
      z-index: $skeleton-container-index;
    }

    &.square {
      border-radius: $border-radius;
    }

    &.circular {
      border-radius: 50%;
    }
  }
}

3、使用demo

<template>
    <div class="home-wrapper">
        <!--仅仅移除骨架屏组件即可-->
        <vueSkeleton v-if="loading"></vueSkeleton>
        <div class="banner skeleton-square">
            <img :src="banner" alt="">
        </div>
        <div class="logo-wrapper">
            <div class="logo skeleton-circular" v-for="a in 3" :key="'logo'+a"></div>
        </div>
        <div class="content"></div>
    </div>
</template>

<script type="text/ecmascript-6">
import vueSkeleton  from './index'
    export default {
       name:'vueSkeleton',
        components:{vueSkeleton},
        data(){
           return{
               loading:true,
               // 如有需要,先默认数据以撑开元素大小和位置
               banner:'http://img5.imgtn.bdimg.com/it/u=1768436016,3486987531&fm=26&gp=0.jpg',
           }
        },
        created() {
           this.getData()
        },
        methods:{
            getData(){
                setTimeout(()=>{
                    this.loading=false
                },300)
            }
        }
    }
</script>

<style lang="scss" scoped>
@import "./index";
.home-wrapper{
    width: 100%;
    .banner{
        width: 100%;
        height: 175px;
    }
    .logo-wrapper{
        width: 100%;
        padding: 0 15px;
        box-sizing: border-box;
        display: flex;
        .logo{
            border-radius: 50%;
            width: 105px;
            height: 105px;
            margin-right: 15px;
            &:last-child{
                margin-right: 0;
            }
        }
    }
    .content{
        width: 100%;
        height: 300px;
    }
}
</style>

4、Taro骨架屏也写了一下

https://blog.csdn.net/u010214074/article/details/90120727

5、github地址:

https://github.com/chenkongming/vue-skeleton

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值