<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="$tn.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="$tn.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="$tn.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.$tn.color.getBackgroundColorStyle(this.elBackgroundColor) }, elBackgroundColorClass() { return this.$tn.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>