uni-app开发微信小程序,根据页面元素动态生成骨架屏

617 阅读2分钟

微信小程序有自动做骨架屏的功能,uni-app貌似没看到,决定自己倒腾一个,上代码,先封一个skeleton组件

<template>
    <div class="skeleton-comp">
        <view v-for="(otem, idx) in skeletonRegion.skeletonRectLists" :key='idx' class="shine"
            :style="{'width': `${otem.width}${unit}`, 'height': `${otem.height}${unit}`, 'left': `${otem.left}${unit}`, 'top': `${otem.top - topBoxHeight}${unit}`}">
        </view>
        <view v-for="(otem, idx) in skeletonRegion.skeletonCircleLists" :key='idx' class="shine flex-center-c circle-box bgc-fff"
            :style="{'width': `${otem.width}${unit}`, 'height': `${otem.height}${unit}`, 'left': `${otem.left}${unit}`, 'top': `${otem.top - topBoxHeight}${unit}`}">
            <view></view>
        </view>
    </div>
</template>

这里的style里面还要加一个position: absolute, 里面的这句- topBoxHeight是因为我的项目有做自定义header,需要把那个自定义盒子的高度去掉,如果不是自定义header,这里这一句就删掉,要不然算出来的高度不对。对哦,别忘了顶级的盒子要相对定位

image.png

接收两个props参数,selector决定用哪一个选择器,unit表示用什么单位

const props = defineProps({
        selector: {
            type: String,
            default: 'skeleton'
        },
        unit: {
            type: String,
            default: 'px'
        }
    })
let skeletonRegion = ref({
    skeletonRectLists: [],
    skeletonCircleLists: []
})
let topBoxHeight = ref(getStorage('topBoxHeight'))

onMounted(() => {
    rectHandle()
    radiusHandle()
})

// 画矩形
const rectHandle = () => {
    uni.createSelectorQuery().selectAll(`.${props.selector} >>> .${props.selector}-rect`).boundingClientRect().exec((res) => {
        skeletonRegion.value.skeletonRectLists = res[0]
    });
}
// 画圆形
const radiusHandle = () => {
    uni.createSelectorQuery().selectAll(`.${props.selector} >>> .${props.selector}-radius`).boundingClientRect().exec((res) => {
        skeletonRegion.value.skeletonCircleLists = res[0]
    });
}

贴上css,做了一点动画效果,一步到位

.skeleton-comp{
    position: relative;
    @keyframes shine {
      from {
          background-position: 0 0 ;
      }
      to {
          background-position: 20rem 0;
      }
    }
    .shine {
      background: rgb(194, 207, 214);
      background-image: linear-gradient(90deg,rgba(255, 255, 255, 0.1) 20%, transparent 25%);
      background-size: 20rem 20rem;
      animation: shine 1s linear infinite;
      position: absolute;
      z-index: 999;
    }

    .wipe {
      overflow: hidden;
      margin: 0 auto;
      display: inline-flex;
      animation-name: wipe;
    }
    @keyframes wipe {
      to { width: 0 }
    }
    .circle-box > view{
        width: 100%;
        height: 100%;
        border-radius: 50%;
        background: rgb(194, 207, 214);
        background-image: linear-gradient(90deg,rgba(255, 255, 255, 0.1) 20%, transparent 25%);
        background-size: 20rem 20rem;
        animation: shine 1s linear infinite;
    }
    .bgc-fff{
        background-color: #fff !important;
    }
}

组件就封好了,具体咋用呢,当然是先引入组件,再用一个属性控制它的显示,后端数据加载成功后,隐藏这个骨架屏

import skeleton from '@/components/skeleton/skeleton.vue'
<skeleton selector="sk" v-if="showSkeleton"></skeleton>

重点来了,注意看这里的class具体咋写的,最外层盒子一定要有一个和你定义的selector值相同的class,然后里面的子盒子,你想画圆形的就用sk-radius,画矩形就用sk-rect

image.png

最后,因为这个组件是根据元素的宽高和定位画盒子的,所以必须要有原始的数据支撑盒子。要不然没宽高就gg

image.png

真正的大佬在这里github.com/jayZOU/skel…