增加骨架屏

This commit is contained in:
lipnggao 2023-09-26 10:38:08 +08:00
parent 59e6b233b6
commit 23d3ef876f
12 changed files with 4578 additions and 3153 deletions

View File

@ -9,7 +9,7 @@
:explosiveDataList="explosiveDataList" :rankingDataList="rankingDataList" :explosiveDataList="explosiveDataList" :rankingDataList="rankingDataList"
:headerListActive="headerListActive" :popularityDataList="popularityDataList" :headerListActive="headerListActive" :popularityDataList="popularityDataList"
:originalDataList="originalDataList" :recommendDataList="recommendDataList" :originalDataList="originalDataList" :recommendDataList="recommendDataList"
:moreBooksList="moreBooksList" :swiperList="swiperList" /> :moreBooksList="moreBooksList" :swiperList="swiperList" :skeletonLoading="skeletonLoading" />
<Member v-if="navBarActive == '2'" /> <Member v-if="navBarActive == '2'" />
</view> </view>
</scroll-view> </scroll-view>
@ -60,6 +60,7 @@
// //
moreBooksList: [], moreBooksList: [],
swiperList: [], swiperList: [],
skeletonLoading: false
} }
}, },
onLoad() {}, onLoad() {},
@ -74,9 +75,10 @@
this.navBarActive = event.currentTarget.dataset.id this.navBarActive = event.currentTarget.dataset.id
}, },
isGetHomeIndex() { isGetHomeIndex() {
uni.showLoading({ // this.skeletonLoading = true;
title: '加载中...' // uni.showLoading({
}); // title: '...'
// });
// uni.$u.post // uni.$u.post
const parameter = { const parameter = {
custom: { custom: {
@ -84,7 +86,8 @@
} }
} }
uni.$u.http.post('/Index', {}, parameter).then((res) => { uni.$u.http.post('/Index', {}, parameter).then((res) => {
uni.hideLoading(); // uni.hideLoading();
this.skeletonLoading = false;
if (res.status == 1) { if (res.status == 1) {
const { const {
ads, ads,

View File

@ -1,7 +1,22 @@
<template> <template>
<view class="content"> <view class="bookCity_home_content">
<view class="my_x_skeleton" v-if="skeletonLoading">
<x-skeleton type="banner" :loading="skeletonLoading" class="step_swiper_skeleton"></x-skeleton>
<x-skeleton type="menu" :loading="skeletonLoading" class="step_operate_skeleton"
:configs="{'gridRows': 1,'gridColumns':4, 'textWidth': '40%'}"></x-skeleton>
<x-skeleton type="waterfall" :loading="skeletonLoading" class="step_explosive_skeleton"
:configs="{'gridRows': 1,'gridColumns':3, 'headWidth': '100%', 'headHeight':'266rpx','headBorderRadius':'8rpx','textRows':2 ,'textRowsGap':'20rpx','textWidth':['100%','80%']}"></x-skeleton>
<x-skeleton type="menu" :loading="skeletonLoading" class="step_ranking_skeleton"
:configs="{'gridRows': 1,'gridColumns':4, 'headWidth': '100%', 'headHeight':'70rpx','textShow':false,'headBorderRadius':'8rpx','gridColumnsGap':'20rpx','padding':'15px 15px 0 15px'}"></x-skeleton>
<x-skeleton type="menu" :loading="skeletonLoading" class="step_ranking_skeleton"
:configs="{'gridRows': 2,'gridColumns':2, 'headWidth': '130rpx', 'headHeight':'180rpx','headBorderRadius':'8rpx','gridColumnsGap':'20rpx','itemDirection':'row', 'textRows':3,'textWidth':['100%','80%','60%'],'textRowsGap':'20rpx','gridRowsGap':'32rpx'}"></x-skeleton>
<x-skeleton type="menu" :loading="skeletonLoading" class="step_ranking_skeleton"
:configs="{'gridRows': 1,'gridColumns':1, 'headWidth': '100%', 'headHeight':'82rpx','headBorderRadius':'8rpx','textShow':false,'padding':'0 15px 15px 15px'}"></x-skeleton>
</view>
<view class="home_content_box" v-if="!skeletonLoading">
<!-- 焦点图 --> <!-- 焦点图 -->
<view class="step_swiper"> <view class="step_swiper">
<!-- style="height: 260rpx;" -->
<u-swiper :list="swiperList" keyName="cover" indicator indicatorMode="line" circular></u-swiper> <u-swiper :list="swiperList" keyName="cover" indicator indicatorMode="line" circular></u-swiper>
</view> </view>
<!--九宫格操作列表 --> <!--九宫格操作列表 -->
@ -17,7 +32,6 @@
<view class="step_explosive"> <view class="step_explosive">
<ExplosiveList :dataList="explosiveDataList" /> <ExplosiveList :dataList="explosiveDataList" />
</view> </view>
<view style="width: 100%"> <view style="width: 100%">
<u-gap height="14rpx" bgColor="#F6F6F6"></u-gap> <u-gap height="14rpx" bgColor="#F6F6F6"></u-gap>
</view> </view>
@ -59,6 +73,7 @@
<CommFooter /> <CommFooter />
</view> --> </view> -->
</view> </view>
</view>
</template> </template>
<script> <script>
@ -135,6 +150,12 @@
default: () => { default: () => {
return []; return [];
} }
},
skeletonLoading:{
type: Boolean,
default: () => {
return true;
}
} }
}, },
data() { data() {
@ -190,11 +211,27 @@
</script> </script>
<style lang="scss"> <style lang="scss">
.content { .bookCity_home_content {
// padding: 0 0 150rpx; // padding: 0 0 150rpx;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
.my_x_skeleton {
.step_swiper_skeleton {
/deep/.x-skeleton__wrapper__head {
height: 260rpx !important;
}
}
.step_operate_skeleton {
/deep/.x-skeleton__wrapper__text {
display: flex;
justify-content: center;
}
}
}
.home_content_box {
.step_swiper { .step_swiper {
width: 100%; width: 100%;
padding: 0 32rpx; padding: 0 32rpx;
@ -252,4 +289,6 @@
margin-bottom: 32rpx; margin-bottom: 32rpx;
} }
} }
}
</style> </style>

View File

@ -23,8 +23,9 @@
<text class="privacy" @tap="toAgreement('2')">古言隐私政策</text> <text class="privacy" @tap="toAgreement('2')">古言隐私政策</text>
</view> </view>
</u-modal> </u-modal>
<u-modal :show="modalShowTwo" title="您需要同意以下协议才能正常使用古言" :showCancelButton="true" cancelColor="#999" confirmText="同意" <u-modal :show="modalShowTwo" title="您需要同意以下协议才能正常使用古言" :showCancelButton="true" cancelColor="#999"
cancelText="不同意并退出" confirmColor="#FF728F" @confirm="toAgreeWith" @cancel="toExitApplication"> confirmText="同意" cancelText="不同意并退出" confirmColor="#FF728F" @confirm="toAgreeWith"
@cancel="toExitApplication">
<view class="slot_content"> <view class="slot_content">
<view>若您不同意很遗憾我们将无法为您提供服务</view> <view>若您不同意很遗憾我们将无法为您提供服务</view>
<view class="user_agreement" @tap="toAgreement('1')">古言用户服务协议</view> <view class="user_agreement" @tap="toAgreement('1')">古言用户服务协议</view>
@ -44,24 +45,28 @@
return { return {
giveService: false, giveService: false,
modalShowOne: false, modalShowOne: false,
modalShowTwo:false modalShowTwo: false
} }
}, },
onShow() { onShow() {
const giveService = myGetStorage('giveService'); const giveService = myGetStorage('giveService');
this.giveService = giveService ? false : true; this.giveService = giveService ? false : true;
this.modalShowOne = giveService ? false : true; this.modalShowOne = giveService ? false : true;
if(giveService){ if (giveService) {
setTimeout(() => {
uni.switchTab({ uni.switchTab({
url: `/pages/bookCity/bookCity/index` url: `/pages/bookCity/bookCity/index`
}) })
}, 1000)
} }
}, },
methods: { methods: {
toAgreeWith() { toAgreeWith() {
setTimeout(() => {
uni.switchTab({ uni.switchTab({
url: `/pages/bookCity/bookCity/index` url: `/pages/bookCity/bookCity/index`
}) })
}, 300)
mySetStorage('giveService', 1); mySetStorage('giveService', 1);
this.modalShowOne = false; this.modalShowOne = false;
this.modalShowTwo = false; this.modalShowTwo = false;
@ -75,7 +80,7 @@
}, },
toAgreement(type) { toAgreement(type) {
uni.navigateTo({ uni.navigateTo({
url:`/pages/agreement/agreement?type=${type}` url: `/pages/agreement/agreement?type=${type}`
}) })
}, },
handelOnceMore() { handelOnceMore() {

View File

@ -91,16 +91,8 @@
uni.navigateTo({ uni.navigateTo({
url: `/pages/login/login?to=2` url: `/pages/login/login?to=2`
}) })
// this.showModal = true;
// uni.showModal({
// title:''
// })
} }
}, },
// modalCancel() {
// this.showModal = false;
// },
modalConfirm() { modalConfirm() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/login/login`, url: `/pages/login/login`,

View File

@ -4,7 +4,7 @@
<!-- @setCatalog="setCatalog" @clickme="clickme" @clickher="clickher" @preload="preloadContent"--> <!-- @setCatalog="setCatalog" @clickme="clickme" @clickher="clickher" @preload="preloadContent"-->
<view class="novelReading_main_con"> <view class="novelReading_main_con">
<view class="novelReading_main_read" <view class="novelReading_main_read"
:style="`background:${theFirstTime ? 'rgba(0,0,0,0.8)' :'transparent'};top:${mainReadTipsTop};bottom:${mainReadTipsBottom}`"> :style="`background:${theFirstTime ? 'rgba(0,0,0,0.8)' :'transparent'};top:${mainReadTipsTop};bottom:${mainReadIsVipBottom ? mainReadIsVipBottom : mainReadTipsBottom}`">
<view class="main_read_item read_item_last" @tap="pageLsatTurning"> <view class="main_read_item read_item_last" @tap="pageLsatTurning">
<text v-if="theFirstTime">上一页</text> <text v-if="theFirstTime">上一页</text>
</view> </view>
@ -50,7 +50,7 @@
<view class="balance_btn_all"> <view class="balance_btn_all">
<view v-if="!token" class="purchaseFull_popup_btn" @click="toPathLogin">新用户登录</view> <view v-if="!token" class="purchaseFull_popup_btn" @click="toPathLogin">新用户登录</view>
<view v-else-if="readChapterInfoObj.chackpay == 2" class="purchaseFull_popup_btn" <view v-else-if="readChapterInfoObj.chackpay == 2" class="purchaseFull_popup_btn"
@click="balanceConfirm">去充值</view> @click="toBalanceConfirm">去充值</view>
<view v-else-if="readChapterInfoObj.chackpay == 3" class="purchaseFull_popup_btn" <view v-else-if="readChapterInfoObj.chackpay == 3" class="purchaseFull_popup_btn"
@click="handelPurchaseFull">需要全本购买 @click="handelPurchaseFull">需要全本购买
</view> </view>
@ -243,7 +243,8 @@
readChapterNextid: '', readChapterNextid: '',
theFirstTime: true, theFirstTime: true,
mainReadTipsTop: 0, mainReadTipsTop: 0,
mainReadTipsBottom: 0 mainReadTipsBottom: 0,
mainReadIsVipBottom: 0
}; };
}, },
onLoad(options) { onLoad(options) {
@ -309,7 +310,6 @@
if (res.status == 1) { if (res.status == 1) {
const resData = res.data; const resData = res.data;
const is_novel_content = resData.novel_content.replace(/<p><\/p>/g, '\n'); const is_novel_content = resData.novel_content.replace(/<p><\/p>/g, '\n');
console.log(is_novel_content,"is_novel_contentis_novel_content")
const isContent = is_novel_content.replace(/<p>/g, ''); const isContent = is_novel_content.replace(/<p>/g, '');
const isContent2 = isContent.replace(/<\/p>/g, ''); const isContent2 = isContent.replace(/<\/p>/g, '');
const readChapterInfoObj = { const readChapterInfoObj = {
@ -323,7 +323,6 @@
isTitle: resData.title isTitle: resData.title
// richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}` // richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
}; };
// this.readChapterInfoObj = readChapterInfoObj;
resolve(readChapterInfoObj); resolve(readChapterInfoObj);
} }
}).catch((err) => { }).catch((err) => {
@ -345,20 +344,7 @@
chapterId = newReadChapterNextid chapterId = newReadChapterNextid
} }
const resTemp = await this.isGetBookInfo(chapterId); const resTemp = await this.isGetBookInfo(chapterId);
let newReadInfoObj = this.isBooksObj(resTemp);; let newReadInfoObj = this.isBooksObj(resTemp);
// if (readChapterInfoObj.chackpay == 1) {
// newReadInfoObj = {
// ...readChapterInfoObj
// }
// callback('success', newReadInfoObj);
// }
// if (readChapterInfoObj.chackpay == 2 || readChapterInfoObj.chackpay == 3) {
// const custom = [`slot:test`];
// newReadInfoObj = {
// ...readChapterInfoObj,
// custom,
// content: '',
// }
callback('success', newReadInfoObj); callback('success', newReadInfoObj);
const sortArr = this.handelReadList([newReadInfoObj]); const sortArr = this.handelReadList([newReadInfoObj]);
this.novelReadingContentText = sortArr; this.novelReadingContentText = sortArr;
@ -372,6 +358,11 @@
this.readChapterLastid = m.lastid; this.readChapterLastid = m.lastid;
this.readChapterNextid = m.nextid; this.readChapterNextid = m.nextid;
this.readChapterInfoObj = m; this.readChapterInfoObj = m;
if (m.isvip == 1) {
this.mainReadIsVipBottom = '360rpx';
} else {
this.mainReadIsVipBottom = 0;
}
} }
}) })
}, },
@ -491,7 +482,6 @@
const sortArr = this.handelReadList(contents); const sortArr = this.handelReadList(contents);
this.novelReadingContentText = sortArr this.novelReadingContentText = sortArr
this.readChapterInfoObj = newReadInfoObj; this.readChapterInfoObj = newReadInfoObj;
// if(type) {
this.$refs.yingbingReadPage.init({ this.$refs.yingbingReadPage.init({
contents: sortArr, contents: sortArr,
start: 0, start: 0,
@ -561,7 +551,7 @@
handelSteUpPopup() { handelSteUpPopup() {
this.stepUpPopupShow = true; this.stepUpPopupShow = true;
}, },
balanceConfirm() { toBalanceConfirm() {
uni.navigateTo({ uni.navigateTo({
url: `/pages/voucherCenter/index` url: `/pages/voucherCenter/index`
}) })

View File

@ -0,0 +1,11 @@
## 1.0.42023-09-02
1、优化一些显示的细节问题
## 1.0.32023-09-01
1、新增动画、时间、颜色等相关属性
## 1.0.22023-08-31
1、回滚到v1.0.0版本,暂时取消动画时间的修改属性
## 1.0.12023-08-31
1、新增动画持续时间属性
2、新增淡出持续时间属性
## 1.0.02023-08-31
第一个版本

View File

@ -0,0 +1,147 @@
export default {
methods: {
bannerConfigs() {
return {
padding: '30rpx',
gridRows: 1,
gridColumns: 1,
gridRowsGap: '40rpx',
gridColumnsGap: '24rpx',
itemDirection: 'row',
itemGap: '30rpx',
itemAlign: 'center',
headShow: true,
headWidth: '100%',
headHeight: '300rpx',
headBorderRadius: '20rpx',
textShow: false,
textRows: 3,
textRowsGap: '20rpx',
textWidth: '100%',
textHeight: '30rpx',
textBorderRadius: '6rpx',
...this.configs
}
},
infoConfigs() {
return {
padding: '30rpx',
gridRows: 1,
gridColumns: 1,
gridRowsGap: '50rpx',
gridColumnsGap: '24rpx',
itemDirection: 'row',
itemGap: '30rpx',
itemAlign: 'flex-start',
headShow: true,
headWidth: '100rpx',
headHeight: '100rpx',
headBorderRadius: '50%',
textShow: true,
textRows: 4,
textRowsGap: '30rpx',
textWidth: ['50%', '100%', '100%', '80%'],
textHeight: ['40rpx', '24rpx', '24rpx', '24rpx'],
textBorderRadius: '6rpx',
...this.configs
}
},
textConfigs() {
return {
padding: '30rpx',
gridRows: 1,
gridColumns: 1,
gridRowsGap: '50rpx',
gridColumnsGap: '24rpx',
itemDirection: 'row',
itemGap: '30rpx',
itemAlign: 'flex-start',
headShow: false,
headWidth: '100rpx',
headHeight: '100rpx',
headBorderRadius: '50%',
textShow: true,
textRows: 4,
textRowsGap: '30rpx',
textWidth: ['50%', '100%', '100%', '80%'],
textHeight: '30rpx',
textBorderRadius: '6rpx',
...this.configs
}
},
menuConfigs() {
return {
padding: '30rpx',
gridRows: 2,
gridColumns: 5,
gridRowsGap: '40rpx',
gridColumnsGap: '40rpx',
itemDirection: 'column',
itemGap: '16rpx',
itemAlign: 'center',
headShow: true,
headWidth: '100rpx',
headHeight: '100rpx',
headBorderRadius: '50%',
textShow: true,
textRows: 1,
textRowsGap: '0rpx',
textWidth: '100%',
textHeight: '24rpx',
textBorderRadius: '6rpx',
...this.configs
}
},
listConfigs() {
return {
padding: '30rpx',
gridRows: 2,
gridColumns: 1,
gridRowsGap: '50rpx',
gridColumnsGap: '24rpx',
itemDirection: 'row',
itemGap: '30rpx',
itemAlign: 'flex-start',
headShow: true,
headWidth: '200rpx',
headHeight: '200rpx',
headBorderRadius: '16rpx',
textShow: true,
textRows: 4,
textRowsGap: '30rpx',
textWidth: ['50%', '100%', '100%', '80%'],
textHeight: ['38rpx', '24rpx', '24rpx', '24rpx'],
textBorderRadius: '6rpx',
...this.configs
}
},
waterfallConfigs() {
return {
padding: '30rpx',
gridRows: 2,
gridColumns: 2,
gridRowsGap: '40rpx',
gridColumnsGap: '24rpx',
itemDirection: 'column',
itemGap: '16rpx',
itemAlign: 'center',
headShow: true,
headWidth: '100%',
headHeight: '400rpx',
headBorderRadius: '12rpx',
textShow: true,
textRows: 3,
textRowsGap: '12rpx',
textWidth: ['40%', '85%', '60%'],
textHeight: ['30rpx', '20rpx', '20rpx'],
textBorderRadius: '6rpx',
...this.configs
}
},
}
}

View File

@ -0,0 +1,324 @@
<template>
<view class="x-skeleton" :style="variableStr">
<!-- 骨架屏 -->
<view
v-if="skeletonLoading"
class="x-skeleton__wrapper"
:class="[ startFadeOut && 'fade-out' ]"
:style="{ padding: skeletonConfigs.padding }"
>
<view
v-for="(row, rowIndex) in gridRowsArr" :key="rowIndex"
class="x-skeleton__wrapper__rows"
:style="{ marginBottom: rowIndex < gridRowsArr.length - 1 ? skeletonConfigs.gridRowsGap : 0 }"
>
<view
v-for="(column, columnIndex) in gridColumnsArr" :key="columnIndex"
class="x-skeleton__wrapper__columns"
:style="{
flexDirection: skeletonConfigs.itemDirection,
alignItems: skeletonConfigs.itemAlign,
marginRight: columnIndex < gridColumnsArr.length - 1 ? skeletonConfigs.gridColumnsGap : 0,
}"
>
<view
v-if="skeletonConfigs.headShow"
class="x-skeleton__wrapper__head"
:class="[ animate && 'animate' ]"
:style="{
width: skeletonConfigs.headWidth,
height: skeletonConfigs.headHeight,
borderRadius: skeletonConfigs.headBorderRadius,
marginRight: (skeletonConfigs.itemDirection == 'row' && skeletonConfigs.textShow) ? skeletonConfigs.itemGap : 0,
marginBottom: (skeletonConfigs.itemDirection == 'column' && skeletonConfigs.textShow) ? skeletonConfigs.itemGap : 0
}"
></view>
<view v-if="skeletonConfigs.textShow" class="x-skeleton__wrapper__text">
<view
v-for="(text, textIndex) in textRowsArr" :key="textIndex"
class="x-skeleton__wrapper__text__row"
:class="[animate && 'animate']"
:style="{
width: text.width,
height: text.height,
borderRadius: skeletonConfigs.textBorderRadius,
marginBottom: textIndex < textRowsArr.length - 1 ? skeletonConfigs.textRowsGap : 0
}"
></view>
</view>
</view>
</view>
</view>
<!-- 插槽 -->
<view v-else>
<slot></slot>
</view>
</view>
</template>
<script>
import XSkeletonConfigs from './x-skeleton-configs.js'
export default {
name: "x-skeleton",
mixins: [ XSkeletonConfigs ],
props: {
//
type: {
type: String,
default: '' //bannerinfotextmenulistwaterfall
},
//
loading: {
type: Boolean,
default: true
},
//
animate: {
type: Boolean,
default: true
},
//
animateTime: {
type: Number | String,
default: 1.8
},
//
fadeOut: {
type: Boolean,
default: true
},
//
fadeOutTime: {
type: Number | String,
default: 0.5
},
//
bgColor: {
type: String,
default: '#EAEDF5'
},
//
highlightBgColor: {
type: String,
default: '#F9FAFF'
},
//
configs: {
type: Object,
default: () => {
return {
// padding: '30rpx', //
// gridRows: 3, //
// gridColumns: 2, //
// gridRowsGap: '40rpx', //
// gridColumnsGap: '24rpx', //
// itemDirection: 'column', //headtextrowcolumn
// itemGap: '16rpx', //headtext
// itemAlign: 'center', //headtextcenterflex-startflex-endbaseline
// headShow: true, //head
// headWidth: '100%', //head
// headHeight: '400rpx', //head
// headBorderRadius: '12rpx', //head
// textShow: true, //
// textRows: 3, //
// textRowsGap: '12rpx', //
// textWidth: ['40%', '85%', '60%'], //
// textHeight: ['30rpx', '20rpx', '20rpx'], //
// textBorderRadius: '6rpx', //
}
}
}
},
computed: {
gridRowsArr() {
return new Array(Number(this.skeletonConfigs?.gridRows || []));
},
gridColumnsArr() {
return new Array(Number(this.skeletonConfigs?.gridColumns || []));
},
textRowsArr() {
if (!this.skeletonConfigs?.textShow) return [];
if (/%$/.test(this.skeletonConfigs.textHeight)) {
console.error('x-skeleton: textHeight参数不支持百分比单位');
}
const rows = []
for (let i = 0; i < this.skeletonConfigs.textRows; i++) {
const { gridRows, textWidth, textHeight } = this.skeletonConfigs;
let item = {},
//
rowWidth = this.isArray(textWidth) ? (textWidth[i] || (i === gridRows - 1 ? '70%' : '100%')) : i === gridRows - 1 ? '70%' : textWidth,
rowHeight = this.isArray(textHeight) ? (textHeight[i] || '30rpx') : textHeight
//
if (/%$/.test(rowWidth)) {
item.width = rowWidth;
} else {
item.width = this.addUnit(rowWidth)
}
item.height = this.addUnit(rowHeight)
rows.push(item)
}
return rows
},
variableStr() {
let keys = ['animateTime', 'fadeOutTime', 'bgColor', 'highlightBgColor'];
let str = keys.map(item => {
if (item.indexOf('Time') > -1) {
return `--${item}:${this[item]}s`
} else {
return `--${item}:${this[item]}`
}
}).join(";");
return str;
}
},
watch: {
loading: {
immediate: true,
handler(value) {
if (value) {
this.skeletonLoading = true;
} else {
if (this.fadeOut) {
this.startFadeOut = true;
setTimeout(() => {
this.skeletonLoading = false;
this.startFadeOut = false;
}, this.fadeOutTime * 1000);
} else {
this.skeletonLoading = false;
}
}
}
},
type: {
immediate: true,
handler(value) {
if (value === 'banner') {
this.skeletonConfigs = this.bannerConfigs();
} else if (value === 'info') {
this.skeletonConfigs = this.infoConfigs();
} else if (value === 'text') {
this.skeletonConfigs = this.textConfigs();
} else if (value === 'menu') {
this.skeletonConfigs = this.menuConfigs();
} else if (value === 'list') {
this.skeletonConfigs = this.listConfigs();
} else if (value === 'waterfall') {
this.skeletonConfigs = this.waterfallConfigs();
} else {
this.skeletonConfigs = this.configs || {};
}
}
}
},
data() {
return {
skeletonConfigs: this.configs || {},
skeletonLoading: this.loading,
startFadeOut: false,
width: 0
};
},
mounted() {
},
methods: {
/**
* @description 是否为数组
* @param {object} value 需要判断的对象
*/
isArray(value) {
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
return Object.prototype.toString.call(value) === '[object Array]'
},
/**
* @description 添加单位如果有rpxupx%px等单位结尾或者值为auto直接返回否则加上px单位结尾
* @param {string|number} value 需要添加单位的值
* @param {string} unit 添加的单位名 比如px
*/
addUnit(value = 'auto', unit = 'px') {
value = String(value);
// uViewnumber
return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value) ? `${value}${unit}` : value;
}
}
}
</script>
<style lang="scss" scoped>
@mixin background {
background: linear-gradient(90deg, var(--bgColor) 25%, var(--highlightBgColor) 37%, var(--bgColor) 50%);
background-size: 400% 100%;
}
.x-skeleton {
width: 100%;
box-sizing: border-box;
.x-skeleton__wrapper {
display: flex;
flex-direction: column;
&__rows {
display: flex;
align-items: center;
justify-content: space-between;
}
&__columns {
display: flex;
align-items: center;
flex: 1;
}
&__head {
width: 100%;
@include background;
}
&__text {
flex: 1;
width: 100%;
&__row {
@include background;
}
}
}
.fade-out {
opacity: 0;
animation: fadeOutAnim var(--fadeOutTime);
}
@keyframes fadeOutAnim {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.animate {
animation: skeletonAnim var(--animateTime) ease infinite;
}
@keyframes skeletonAnim {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
}
</style>

View File

@ -0,0 +1,84 @@
{
"id": "x-skeleton",
"displayName": "skeleton骨架屏可任意配置-易用-灵活-动画)",
"version": "1.0.4",
"description": "x-skeleton骨架屏可随意配置内容扩展性强简单易用内含常用的骨架类型",
"keywords": [
"skeleton",
"骨架屏",
"加载效果",
"vue",
"微信小程序"
],
"repository": "",
"engines": {
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,93 @@
# x-skeleton
# 功能介绍
- 支持 H5、微信小程序其他端未测试过
- 使用简单、灵活,高度自定义
- 加载时支持动画
- 消失时加了动画,不再生硬切换页面
- 支持绝大部分常用场景:
1、轮播图
2、个人信息
3、段落
4、菜单
5、列表
6、瀑布流
7、自定义...
# 属性说明
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
| ------- | --------------------------------------------------- | ------- | ------ | --- |
| type | 骨架类型,为空时是完全自定义 | String | - |banner轮播图、info个人信息、text段落、menu菜单、list列表、waterfall瀑布流|
| loading | 是否显示骨架占位图设置为false将会展示子组件内容 | Boolean | true |true、false|
| animate | 是否开启动画效果 | Boolean | true |true、false|
| animateTime | 动画效果持续时间,单位秒 | Number \| String | 1.8 |-|
| fadeOut | 是否开启淡出动画 | Boolean | true |true、false|
| fadeOutTime | 淡出效果持续时间,单位秒 | Number \| String | 0.5 |-|
| bgColor | 骨架的背景色 | String | #EAEDF5 |-|
| highlightBgColor | 骨架的动画高亮背景色 | String | #F9FAFF |-|
| configs | 自定义配置,具体看下方 | Object | {} |-|
## configs参数说明
| 参数 | 说明 | 类型 |
| ------- | --------------------------------------------------- | ------- |
| padding | 骨架内边距,同 css 的 padding | String |
| gridRows | 行数 | Number |
| gridColumns | 列数 | Number |
| gridRowsGap | 行间隔 | String |
| gridColumnsGap | 竖间距 | String |
| itemDirection | head与text之间的排列方向row、column | String |
| itemGap | head与text之间的间隔 | String |
| itemAlign | head与text之间的纵轴对齐方式同 flex 的align-itemscenter、flex-start、flex-end等 | String |
| headShow | head是否展示 | Boolean |
| headWidth | head宽度支持百分比 | String |
| headHeight | head高度 | String |
| headBorderRadius | head圆角支持百分比 | String |
| textShow | text是否展示 | Boolean |
| textRows | text的行数 | Number |
| textRowsGap | text间距 | String |
| textWidth | text的宽度可以为百分比数值带单位字符串等可通过数组传入指定每个段落行的宽度 | String \| Array \| Number |
| textHeight | text的高度可以为数值带单位字符串等可通过数组传入指定每个段落行的高度 | String \| Array \| Number |
| textBorderRadius | text的圆角支持百分比 | String |
大部分情况下,直接指定相应的 type 已经够用了,如果大家想进行样式的微调、完全自定义可通过设置 configs 来实现。
简单解释一下这些参数(右边有结构布局图示):
布局总共分成 4 块,分别是 grid、item、head、text。
1、grid包含 item指定每一行有多少个 item每一列有多少个 item
2、item包含 head、text可设置他们之间的排列方式、间距
3、head一个 item 只有一个 head可设置宽高、圆角
4、text一个 item 可以有多行 text可分别设置宽高、圆角、间距
# 使用示例
```html
<x-skeleton type="banner" :loading="loading">
<view>我是轮播图</view>
</x-skeleton>
```
```js
export default {
data() {
return {
loading: true,
}
},
onLoad() {
setTimeout(() => {
this.loading = false;
}, 2000);
},
}
```
更多用法请下载查看示例代码,有问题可以留言

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long