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