255 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
		
		
			
		
	
	
			255 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
|  | <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> |