<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>
|