<template> 
 | 
  <view 
 | 
    v-if="show" 
 | 
    class="tn-skeleton-class tn-skeleton" 
 | 
    :class="[backgroundColorClass]" 
 | 
    :style="[skeletonStyle]" 
 | 
    @touchmove.stop.prevent 
 | 
  > 
 | 
    <view 
 | 
      v-for="(item, index) in rectNodes" 
 | 
      :key="$t.uuid()" 
 | 
      class="tn-skeleton__item tn-skeleton__item--rect" 
 | 
      :class="[elBackgroundColorClass, {'tn-skeleton__item--fade': animation}]" 
 | 
      :style="[itemStyle('rect', item)]" 
 | 
    ></view> 
 | 
    <view 
 | 
      v-for="(item, index) in circleNodes" 
 | 
      :key="$t.uuid()" 
 | 
      class="tn-skeleton__item tn-skeleton__item--circle" 
 | 
      :class="[elBackgroundColorClass, {'tn-skeleton__item--fade': animation}]" 
 | 
      :style="[itemStyle('circle', item)]" 
 | 
    ></view> 
 | 
    <view 
 | 
      v-for="(item, index) in filletNodes" 
 | 
      :key="$t.uuid()" 
 | 
      class="tn-skeleton__item tn-skeleton__item--fillet" 
 | 
      :class="[elBackgroundColorClass, {'tn-skeleton__item--fade': animation}]" 
 | 
      :style="[itemStyle('fillet', item)]" 
 | 
    ></view> 
 | 
  </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
  import componentsColorMixin from '../../libs/mixin/components_color.js' 
 | 
  export default { 
 | 
    name: 'tn-skeleton', 
 | 
    mixins: [ componentsColorMixin ], 
 | 
    props: { 
 | 
      // 显示骨架屏 
 | 
      show: { 
 | 
        type: Boolean, 
 | 
        default: false 
 | 
      }, 
 | 
      // 需要渲染的元素背景颜色 
 | 
      elBackgroundColor: { 
 | 
        type: String, 
 | 
        default: '' 
 | 
      }, 
 | 
      // 开启加载动画 
 | 
      animation: { 
 | 
        type: Boolean, 
 | 
        default: true 
 | 
      }, 
 | 
      // 矩形元素自定义样式 
 | 
      rectCustomStyle: { 
 | 
        type: Object, 
 | 
        default() { 
 | 
          return {} 
 | 
        } 
 | 
      }, 
 | 
      // 圆形元素自定义样式 
 | 
      circleCustomStyle: { 
 | 
        type: Object, 
 | 
        default() { 
 | 
          return {} 
 | 
        } 
 | 
      }, 
 | 
      // 圆角元素自定义样式 
 | 
      filletCustomStyle: { 
 | 
        type: Object, 
 | 
        default() { 
 | 
          return {} 
 | 
        } 
 | 
      } 
 | 
    }, 
 | 
    computed: { 
 | 
      elBackgroundColorStyle() { 
 | 
        return this.$t.color.getBackgroundColorStyle(this.elBackgroundColor) 
 | 
      }, 
 | 
      elBackgroundColorClass() { 
 | 
        return this.$t.color.getBackgroundColorInternalClass(this.elBackgroundColor) 
 | 
      }, 
 | 
      // 骨架屏样式 
 | 
      skeletonStyle() { 
 | 
        let style = {} 
 | 
        style.width = this.skeletonWidth + 'px' 
 | 
        style.height = this.skeletonHeight + 'px' 
 | 
        if (this.backgroundColorStyle) { 
 | 
          style.backgroundColor = this.backgroundColorStyle 
 | 
        } 
 | 
        style.left = this.left + 'px' 
 | 
        style.top = this.top + 'px' 
 | 
        return style 
 | 
      }, 
 | 
      // 元素样式 
 | 
      itemStyle() { 
 | 
        return (type, item) => { 
 | 
          let style = {} 
 | 
          style.width = item.width + 'px' 
 | 
          style.height = item.height + 'px' 
 | 
          if (this.elBackgroundColorStyle) { 
 | 
            style.backgroundColor = this.elBackgroundColorStyle 
 | 
          } 
 | 
          style.left = (item.left - this.left) + 'px' 
 | 
          style.top = (item.top - this.top) + 'px' 
 | 
          if (type === 'rect') { 
 | 
            Object.assign(style, this.rectCustomStyle) 
 | 
          } else if (type === 'circle') { 
 | 
            style.borderRadius = (item.width / 2) + 'px' 
 | 
            Object.assign(style, this.circleCustomStyle) 
 | 
          } else if (type === 'fillet') { 
 | 
            Object.assign(style, this.filletCustomStyle) 
 | 
          } 
 | 
          return style 
 | 
        } 
 | 
      } 
 | 
    }, 
 | 
    data() { 
 | 
      return { 
 | 
        // 骨架屏宽度 
 | 
        skeletonWidth: 750, 
 | 
        // 骨架屏高度 
 | 
        skeletonHeight: 1500, 
 | 
        // 圆角元素 
 | 
        filletNodes: [], 
 | 
        // 圆形元素 
 | 
        circleNodes: [], 
 | 
        // 矩形元素 
 | 
        rectNodes: [], 
 | 
        // 元素偏移位置 
 | 
        top: 0, 
 | 
        left: 0 
 | 
      } 
 | 
    }, 
 | 
    mounted() { 
 | 
      this.$nextTick(() => { 
 | 
        // 获取系统信息 
 | 
        const systemInfo = uni.getSystemInfoSync() 
 | 
        this.skeletonWidth = systemInfo.safeArea.width 
 | 
        this.skeletonHeight = systemInfo.safeArea.height 
 | 
        this.selectQueryInfo() 
 | 
      }) 
 | 
    }, 
 | 
    methods: { 
 | 
      // 查询节点信息 
 | 
      selectQueryInfo() { 
 | 
        // 获取整个父容器的宽高作为骨架屏的宽高 
 | 
        // 在微信小程序中,如果把骨架屏放入组件使用,需要in(this)上下文为父组件才有效 
 | 
        let query = null 
 | 
        // 在微信小程序中,如果把骨架屏放入组件使用,需要in(this)上下文为父组件才有效 
 | 
        // #ifdef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery().in(this.$parent) 
 | 
        // #endif 
 | 
        // #ifndef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery() 
 | 
        // #endif 
 | 
        query.selectAll('.tn-skeleton').boundingClientRect().exec((res) => { 
 | 
          console.log(res); 
 | 
          this.skeletonWidth = res[0][0].width 
 | 
          this.skeletonHeight = res[0][0].height 
 | 
          this.top = res[0][0].bottom - res[0][0].height 
 | 
          this.left = res[0][0].left 
 | 
        }) 
 | 
         
 | 
        // 获取元素列表 
 | 
        this.getRectElements() 
 | 
        this.getCircleElements() 
 | 
        this.getFillteElements() 
 | 
      }, 
 | 
      // 矩形元素列表 
 | 
      getRectElements() { 
 | 
        let query = null 
 | 
        // 在微信小程序中,如果把骨架屏放入组件使用,需要in(this)上下文为父组件才有效 
 | 
        // #ifdef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery().in(this.$parent) 
 | 
        // #endif 
 | 
        // #ifndef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery() 
 | 
        // #endif 
 | 
        query.selectAll('.tn-skeleton-rect').boundingClientRect().exec((res) => { 
 | 
          this.rectNodes = res[0] 
 | 
        }) 
 | 
      }, 
 | 
      // 圆形元素列表 
 | 
      getCircleElements() { 
 | 
        let query = null 
 | 
        // 在微信小程序中,如果把骨架屏放入组件使用,需要in(this)上下文为父组件才有效 
 | 
        // #ifdef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery().in(this.$parent) 
 | 
        // #endif 
 | 
        // #ifndef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery() 
 | 
        // #endif 
 | 
        query.selectAll('.tn-skeleton-circle').boundingClientRect().exec((res) => { 
 | 
          this.circleNodes = res[0] 
 | 
        }) 
 | 
      }, 
 | 
      // 圆角元素列表 
 | 
      getFillteElements() { 
 | 
        let query = null 
 | 
        // 在微信小程序中,如果把骨架屏放入组件使用,需要in(this)上下文为父组件才有效 
 | 
        // #ifdef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery().in(this.$parent) 
 | 
        // #endif 
 | 
        // #ifndef MP-WEIXIN 
 | 
        query = uni.createSelectorQuery() 
 | 
        // #endif 
 | 
        query.selectAll('.tn-skeleton-fillet').boundingClientRect().exec((res) => { 
 | 
          this.filletNodes = res[0] 
 | 
        }) 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
</script> 
 | 
  
 | 
<style lang="scss" scoped> 
 | 
  .tn-skeleton { 
 | 
    position: absolute; 
 | 
    z-index: 9998; 
 | 
    overflow: hidden; 
 | 
    background-color: #FFFFFF; 
 | 
     
 | 
    &__item { 
 | 
      position: absolute; 
 | 
      background-color: #F0F0F0; 
 | 
       
 | 
      &--fillet { 
 | 
        border-radius: 10rpx; 
 | 
      } 
 | 
       
 | 
      &--fade { 
 | 
        width: 100%; 
 | 
        height: 100%; 
 | 
        background-color: #E6E6E6; 
 | 
        animation-duration: 1.5s; 
 | 
        animation-name: blink; 
 | 
        animation-timing-function: ease-in-out; 
 | 
        animation-iteration-count: infinite; 
 | 
      } 
 | 
    } 
 | 
  } 
 | 
   
 | 
  @keyframes blink { 
 | 
    0% { 
 | 
      opacity: 1; 
 | 
    } 
 | 
    50% { 
 | 
      opacity: 0.4; 
 | 
    } 
 | 
    100% { 
 | 
      opacity: 1; 
 | 
    } 
 | 
  } 
 | 
</style> 
 |