10 changed files with 439 additions and 212 deletions
Binary file not shown.
Binary file not shown.
@ -1,183 +0,0 @@ |
|||
<template> |
|||
<view class="wrap"> |
|||
<view class="homeBox"> |
|||
<scroll-view :enable-flex="true" :scroll-left="scrollLeft" :throttle="false" scroll-with-animation scroll-x> |
|||
<view class="tabBox u-skeleton"> |
|||
<view :key="index" @click="changeIndex(index)" class="tab-item" v-for="(item, index) in roles"> |
|||
<view :class="setColor(item.isMine, tabIndex, index)" class="tab-children u-skeleton-rect">{{ item.value }}</view> |
|||
</view> |
|||
</view> |
|||
<u-skeleton :animation="true" :loading="loading" bgcolor="#fff"></u-skeleton> |
|||
</scroll-view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'RoleList', |
|||
data() { |
|||
return { |
|||
tabIndex: 0, //当前访问的 index 默认为0 |
|||
tabList: [], //tab dom节点集合 |
|||
scrollLeft: 0, //scrollview需要滚动的距离 |
|||
roles: [ |
|||
{ id: 1, value: '项目经理', isMine: 0 }, |
|||
{ id: 2, value: '运维', isMine: 0 }, |
|||
{ id: 3, value: '导师一', isMine: 1 }, |
|||
{ id: 4, value: '导师二', isMine: 1 }, |
|||
{ id: 5, value: '导师三', isMine: 1 }, |
|||
{ id: 6, value: '导师四', isMine: 1 }, |
|||
{ id: 7, value: '导师五', isMine: 1 }, |
|||
{ id: 8, value: '导师六', isMine: 1 }, |
|||
{ id: 9, value: '导师七', isMine: 1 }, |
|||
{ id: 10, value: '导师八', isMine: 1 }, |
|||
], |
|||
loading: true, // 是否显示骨架屏组件 |
|||
}; |
|||
}, |
|||
|
|||
mounted() { |
|||
this.init(); |
|||
setTimeout(() => { |
|||
this.loading = false; |
|||
console.log('this.loading: ', this.loading); |
|||
}, 5000); |
|||
}, |
|||
|
|||
methods: { |
|||
init() { |
|||
const data = document.getElementsByClassName('tab-children'); |
|||
// TODO 第一步 获取当前所以子元素 并插入到 tabList 列表中 |
|||
data.forEach(item => { |
|||
this.tabList.push({ width: item.clientWidth, left: item.offsetLeft }); |
|||
}); |
|||
}, |
|||
|
|||
changeIndex(index) { |
|||
//改变index 即手动点击切换 我在此时将当前元素赋值给左边距 实现自动滚动 |
|||
this.tabIndex = index; |
|||
//当前滚动的位置 |
|||
let left = 0; |
|||
let screenWidth = window.screen.width; |
|||
for (let i = 0; i < index; i++) { |
|||
left += this.tabList[i].width + this.tabList[i].left * 2; |
|||
} |
|||
left += this.tabList[index].width; |
|||
this.scrollLeft = left - screenWidth / 2; |
|||
}, |
|||
|
|||
// 设置文字颜色 |
|||
setColor(isMine, tabIndex, index) { |
|||
if (isMine === 1 && tabIndex === index) { |
|||
return 'default-tab-choice'; |
|||
} |
|||
if (isMine === 1 && tabIndex !== index) { |
|||
return 'default-tab-item'; |
|||
} |
|||
if (isMine === 0 && tabIndex === index) { |
|||
return 'tab-choice'; |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
$tabChoiceColor: #f40; //设置选中文字和底部下划线背景颜色 |
|||
$max: 100%; |
|||
|
|||
// 这是最外层盒子 |
|||
.wrap { |
|||
position: relative; |
|||
background: #f7f7f7; |
|||
} |
|||
|
|||
.homeBox { |
|||
// 对此盒子进行 sticky 粘性定位 |
|||
position: sticky; |
|||
top: 0; |
|||
background: #fff; //设置背景 否则会透明 |
|||
/* #ifdef H5 */ |
|||
//粘性定位 在h5下 加 原生头部高度 44px |
|||
top: 88rpx; |
|||
|
|||
/* #endif */ |
|||
.tabBox { |
|||
position: relative; |
|||
white-space: nowrap; |
|||
// height: 88rpx; |
|||
|
|||
/* #ifdef MP-TOUTIAO */ |
|||
/* #endif */ |
|||
.tab-item { |
|||
padding: 15rpx 24rpx; |
|||
position: relative; |
|||
display: inline-block; |
|||
text-align: center; |
|||
font-size: 30rpx; |
|||
transition-property: background-color, width; |
|||
} |
|||
|
|||
.default-tab-item { |
|||
color: $tabChoiceColor; |
|||
} |
|||
|
|||
.default-tab-choice { |
|||
//当前选中 基于此类名给与底部选中框 |
|||
position: relative; |
|||
color: $tabChoiceColor; |
|||
font-weight: 600; |
|||
} |
|||
.default-tab-choice:before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -14rpx; |
|||
width: 100%; |
|||
height: 6rpx; |
|||
border-radius: 2rpx; |
|||
background: $tabChoiceColor; |
|||
} |
|||
|
|||
.tab-choice { |
|||
//当前选中 基于此类名给与底部选中框 |
|||
position: relative; |
|||
color: $uni-color-primary; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.tab-choice:before { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 0; |
|||
bottom: -14rpx; |
|||
width: 100%; |
|||
height: 6rpx; |
|||
border-radius: 2rpx; |
|||
background: $uni-color-primary; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// // 删除 底部滚动条 |
|||
/* #ifndef APP-NVUE */ |
|||
::-webkit-scrollbar, |
|||
::-webkit-scrollbar, |
|||
::-webkit-scrollbar { |
|||
display: none; |
|||
width: 0 !important; |
|||
height: 0 !important; |
|||
-webkit-appearance: none; |
|||
background: transparent; |
|||
} |
|||
|
|||
/* #endif */ |
|||
/* #ifdef H5 */ |
|||
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条 |
|||
scroll-view ::v-deep ::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
|
|||
/* #endif */ |
|||
</style> |
@ -0,0 +1,84 @@ |
|||
# skeleton组件 |
|||
|
|||
### 1.描述 |
|||
> 此组件用于加载数据时占位图显示,跟vant-ui骨架屏用法相似,但比vant-ui更灵活 |
|||
|
|||
|
|||
|
|||
### 2.用法 |
|||
|
|||
- 基本用法 |
|||
|
|||
代码: |
|||
```vue |
|||
//基本用法 |
|||
<skeleton :row="3" animate :loading="loading" > |
|||
<view> |
|||
content |
|||
</view> |
|||
</skeleton> |
|||
``` |
|||
|
|||
|
|||
- **显示 title ——通过 **title 属性显示title占位图 |
|||
|
|||
代码: |
|||
```vue |
|||
//显示 title——通过 title 属性显示title占位图 |
|||
<skeleton :row="3" title animate :loading="loading"> |
|||
<view> |
|||
content |
|||
</view> |
|||
</skeleton> |
|||
``` |
|||
|
|||
|
|||
- 显示头像(上面)——通过avatar=‘top’让头像的占位图上面显示 |
|||
|
|||
代码: |
|||
```vue |
|||
<skeleton :avatar="top" avatarAlign="left" :row="3" animate :loading="loading" style="margin-top:24rpx;"> |
|||
<view> |
|||
content |
|||
</view> |
|||
</skeleton> |
|||
``` |
|||
|
|||
|
|||
- 显示头像(左边)——通过avatar=‘left’让头像的占位图左边显示 |
|||
|
|||
代码: |
|||
```vue |
|||
<skeleton title :avatar="left" :row="3" animate :loading="loading" style="margin-top:24rpx;"> |
|||
<view> |
|||
content |
|||
</view> |
|||
</skeleton> |
|||
``` |
|||
|
|||
|
|||
- 显示banner**——通过 **banner属性显示banner占位图(只显示banner,不显示内容占位图时设置row="0") |
|||
|
|||
代码: |
|||
```vue |
|||
<skeleton banner :row="0" animate :loading="loading" style="margin-top:24rpx;"> |
|||
<view> |
|||
content |
|||
</view> |
|||
</skeleton> |
|||
``` |
|||
### |
|||
### 3. API |
|||
### Props |
|||
| **属性名** | **说明** | **类型** | **默认值** | 可取值 | |
|||
| --- | --- | --- | --- | --- | |
|||
| loading | 是否显示骨架屏 | Boolean | true | true/false | |
|||
| row | 段落行数 | Number | String | 3 | 0表示不展现 | |
|||
| rowWidth | 段落行宽度 | Boolean | Number | '100%' | | |
|||
| title | 是否显示标题 | Boolean | String | false | | |
|||
| banner | 是否显示banner | Boolean | String | false | | |
|||
| animate | 是否开启动画 | Boolean | String | false | | |
|||
| avatar | 头像位置 | Boolean | String | ''空 | left/top | |
|||
| avatarSize | 头像大小 | String | - | | |
|||
| avatarShape | 头像形状 | String | circle | circle/round | |
|||
|
@ -0,0 +1,186 @@ |
|||
<template> |
|||
<view> |
|||
<view :class="[avatarClass, animationClass]" class="lx-skeleton" v-show="loading"> |
|||
<view :class="[avatarShapeClass, bannerClass]" :style="{ width: avatarSize, height: avatarSize }" class="avatar-class"></view> |
|||
<view :style="{ width: rowWidth }" class="row"> |
|||
<view class="row-class lx-skeleton_title" v-if="title"></view> |
|||
<view :key="index" class="row-class" v-for="(item, index) in row"></view> |
|||
</view> |
|||
</view> |
|||
<slot v-if="!loading"></slot> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
/** |
|||
* skeleton 骨架屏 |
|||
* @description 用于加载数据时占位图显示,跟Vant-UI用法相似,但比Vant-UI更灵活 |
|||
* @property {Boolean} loading 是否显示骨架屏,默认为true |
|||
* @property {Number | String} row 段落行数,默认为3 |
|||
* @property {Boolean | Number} rowWidth 段落行宽度,默认为100% |
|||
* @property {Boolean | String} title 是否显示标题,默认为false |
|||
* @property {Boolean | String} banner 是否显示banner,默认为false |
|||
* @property {Boolean | String} animate 是否开启动画,默认为false |
|||
* @property {Boolean | String} avatar 头像位置 |
|||
* @property {String} avatarSize 头像大小 |
|||
* @property {String} avatarShape 头像形状,默认为circle |
|||
* |
|||
* */ |
|||
export default { |
|||
props: { |
|||
loading: { |
|||
type: Boolean, |
|||
default: true, |
|||
}, |
|||
row: { |
|||
type: Number, |
|||
default: 3, |
|||
}, |
|||
title: { |
|||
type: String, |
|||
default: '', |
|||
}, |
|||
avatar: { |
|||
type: String, |
|||
default: '', |
|||
}, |
|||
animate: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
avatarSize: { type: String }, |
|||
rowWidth: { |
|||
type: String, |
|||
default: '100%', |
|||
}, |
|||
avatarShape: { |
|||
type: String, |
|||
default: 'circle', |
|||
}, |
|||
banner: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
// avator-size:{ |
|||
// type: String, |
|||
// defualt: '32px' |
|||
// } |
|||
}, |
|||
computed: { |
|||
avatarClass() { |
|||
if (this.avatar == 'top') { |
|||
return ['lx-skeleton_avator__top']; |
|||
} else if (this.avatar == 'left') { |
|||
return ['lx-skeleton_avator__left']; |
|||
} else { |
|||
return ''; |
|||
} |
|||
}, |
|||
animationClass() { |
|||
return [this.animate ? 'lx-skeleton_animation' : '']; |
|||
}, |
|||
slotClass() { |
|||
return [!this.loading ? 'show' : 'hide']; |
|||
}, |
|||
avatarShapeClass() { |
|||
return [this.avatarShape == 'round' ? 'lx-skeleton_avator__round' : '']; |
|||
}, |
|||
bannerClass() { |
|||
return [this.banner ? 'lx-skeleton_banner' : '']; |
|||
}, |
|||
}, |
|||
data() { |
|||
return {}; |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.lx-skeleton { |
|||
background-color: #fff; |
|||
padding: 12px; |
|||
} |
|||
|
|||
.lx-skeleton_avator__left { |
|||
display: flex; |
|||
width: 100%; |
|||
} |
|||
|
|||
.lx-skeleton_avator__left .avatar-class, |
|||
.lx-skeleton_avator__top .avatar-class { |
|||
background-color: #f2f3f5; |
|||
border-radius: 50%; |
|||
width: 32px; |
|||
height: 32px; |
|||
} |
|||
|
|||
.lx-skeleton_avator__left .avatar-class.lx-skeleton_avator__round, |
|||
.lx-skeleton_avator__top .avatar-class.lx-skeleton_avator__round { |
|||
border-radius: 0; |
|||
width: 32px; |
|||
height: 32px; |
|||
} |
|||
|
|||
.lx-skeleton_avator__left .avatar-class { |
|||
margin-right: 16px; |
|||
} |
|||
|
|||
.lx-skeleton_avator__top .avatar-class { |
|||
margin: 0 auto 12px auto; |
|||
} |
|||
|
|||
.row-class { |
|||
width: 100%; |
|||
height: 16px; |
|||
background-color: #f2f3f5; |
|||
} |
|||
|
|||
.row-class:not(:first-child) { |
|||
margin-top: 12px; |
|||
} |
|||
|
|||
.row { |
|||
flex: 1; |
|||
} |
|||
|
|||
.lx-skeleton_avator__left .row { |
|||
width: calc(100% - 48px); |
|||
} |
|||
|
|||
.row-class:nth-last-child(1) { |
|||
width: 60%; |
|||
} |
|||
|
|||
.lx-skeleton_animation .row-class { |
|||
animation-duration: 1.5s; |
|||
animation-name: blink; |
|||
animation-timing-function: ease-in-out; |
|||
animation-iteration-count: infinite; |
|||
} |
|||
|
|||
@keyframes blink { |
|||
50% { |
|||
opacity: 0.6; |
|||
} |
|||
} |
|||
|
|||
.lx-skeleton_title { |
|||
width: 40%; |
|||
} |
|||
|
|||
.show { |
|||
display: block; |
|||
} |
|||
|
|||
.hide { |
|||
display: none; |
|||
} |
|||
|
|||
.lx-skeleton .lx-skeleton_banner { |
|||
width: 92%; |
|||
margin: 10px auto; |
|||
height: 64px; |
|||
border-radius: 0; |
|||
background-color: #f2f3f5; |
|||
} |
|||
</style> |
@ -0,0 +1,45 @@ |
|||
<template> |
|||
<view> |
|||
<nav-bar title="骨架屏"></nav-bar> |
|||
<view class="content"> |
|||
基础用法 |
|||
<skeleton :row="3" animate :loading="loading"> |
|||
<view> content1 </view> |
|||
</skeleton> |
|||
显示 title |
|||
<skeleton :row="3" title animate :loading="loading"> |
|||
<view> content2 </view> |
|||
</skeleton> |
|||
显示头像(上面) |
|||
<skeleton :avatar="avatarTop" :row="3" animate :loading="loading" style="margin-top: 24rpx"> |
|||
<view> content3 </view> |
|||
</skeleton> |
|||
显示头像(左面) |
|||
<skeleton title :avatar="avatarLeft" :row="3" animate :loading="loading" style="margin-top: 24rpx"> |
|||
<view> content4 </view> |
|||
</skeleton> |
|||
显示banner |
|||
<skeleton banner :row="0" animate :loading="loading" style="margin-top: 24rpx"> |
|||
<view> content5 </view> |
|||
</skeleton> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
loading: true, // 是否显示骨架屏组件 |
|||
avatarTop: 'top', |
|||
avatarLeft: 'left', |
|||
}; |
|||
}, |
|||
onLoad() { |
|||
// 通过延时模拟向后端请求数据,调隐藏骨架屏 |
|||
setTimeout(() => { |
|||
this.loading = false; |
|||
}, 3000); |
|||
}, |
|||
}; |
|||
</script> |
@ -0,0 +1,12 @@ |
|||
{ |
|||
"id": "ls-skeleton", |
|||
"name": "skeleton骨架屏(可随意配置骨架内容结构)", |
|||
"version": "1.0.0", |
|||
"description": "可随意配置骨架内容结构的骨架屏插件,自由度高,复用性强,扩展性强", |
|||
"keywords": [ |
|||
"动画效果", |
|||
"VUE", |
|||
"NVUE", |
|||
"微信小程序" |
|||
] |
|||
} |
Loading…
Reference in new issue