一、需求与背景
这边的需求是做一个手风琴样式的列表,需要有手风琴的动画效果,只能展开一个,列表item的个数和列表item内展开的item个数都是未知的(从后端获取的)。
发现uniapp组件提供的手风琴列表没法自定义title的样式(就是列表item中的样式),所以需要自己实现一个。
二、效果
先上效果
是有逐渐展开的动画效果的,懒得放动图
三、代码
因为我这里元素过多了有点,所以看起来比较复杂,但其实关键代码就是框架上的那几行,分解来看还是很清晰的
1.html部分
<view class="item" v-for="(item,index) in contentList" :key="index">
<view @tap="handleClick(index)" class="title">
<view class="info-box">
<view class="up-box">
<view class="name-box">{{item.studentName}}</view>
<view
v-show="tabIndex == 3"
class="flag-box"
:style="{ background: `${item.bgColor}` ,color:`${item.fontColor}`}">
{{
item.exerciseIntensity === 1
? '适中'
: item.exerciseIntensity === 2
? '过量'
: item.exerciseIntensity === 3
? '不足'
: '未采集'
}}</view>
</view>
<view class="down-box">
<view>学号</view>
<view style="margin-left:20rpx">{{item.studentCode}}</view>
<view class="sex">性别 {{item.studentGender===1?'男':item.studentGender===2?'女':'--'}}</view>
</view>
</view>
<img
:class="item.open?'down':'up'"
:src="require(`@/static/images/icon/down3.png`)"/>
</view>
<view class="p_item" :style="{maxHeight:(item.open && item.exerciseIntensity!=null) ? maxHeight : 0}">
<view @click="toHeart(item,subIndex)" class="sub-box" v-for="(sub,subIndex) in item.sportTimeSlotDataList" :key="subIndex" >
<view class="up-box">
<view>
{{sub.sportTimeSlotBeginToEnd}}
</view>
</view>
<view class="down-box">
<view>最高心率 {{sub.maxHeart}}bpm</view>
<view style="margin-left:50rpx">最低心率 {{sub.minHeart}}bpm</view>
<view style="margin-left:50rpx">平均心率 {{sub.averageHeart}}bpm</view>
</view>
</view>
</view>
</view>
2.js部分
contentList就画了个关键结构,其他的按需增加即可
data() {
return {
maxHeight: 0,//运动量展开那需要的高度
contentList: [
{
open:false,
sportTimeSlotDataList:[]
},
{
open:false,
sportTimeSlotDataList:[]
},
{
open:false,
sportTimeSlotDataList:[]
},
],
};
},
methods: {
// 获取dom
getRect(context, selector) {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(context);
query
.select(selector)
.boundingClientRect((data) => {
resolve(data);
})
.exec();
});
},
handleClick(index) {//运动量
this.contentList[index].open = !this.contentList[index].open;
if (this.contentList[index].open == true) {
for (var i = 0; i < this.contentList.length; i++) {
if (i == index) {
this.contentList[i].open = true;
} else {
this.contentList[i].open = false;
}
}
}
this.getRect(this, '.sub-box').then(res => {
// 加了20是因为每个item有一个margin-top:20,要算进去,不然就没间隔了,所以如果要是改动的话这块也要跟着改
var elLength=this.contentList[index].sportTimeSlotDataList.length;
this.maxHeight = ((parseInt(res.height)+20)* elLength).toString() + 'px';
})
},
}
3.css部分
关键在于动画效果:箭头的up、down,还有子item的展开效果p_item
其他的都是按需求来的都可以改
.item {
display: flex;
flex-direction: column; //决定了下拉框在下面
background-color: #fff;
.title{
display: flex;
flex-direction: row;//左部分是信息,右部分是箭头
justify-content: space-between;
.info-box{
display: flex;
flex-direction: column;//上部分是姓名和运动量特征,下部分是学号和性别
width:500rpx;
justify-content:flex-start;
.up-box{
display: flex;
flex-direction: row;
margin-top:20rpx;//如果这里改了handleClick算高度那里也要改
align-items:flex-end;
.name-box{
font-weight: 500;
font-size: 26rpx;
}
.flag-box{
margin-left : 15rpx;
width: 58rpx;
height: 32rpx;
font-size: 18rpx;
text-align: center;
line-height: 31rpx;
border-radius: 6rpx;
}
.run-box{
font-size: 26rpx;
margin-left:25rpx;
}
.divider{
margin-left:25rpx;
margin-bottom: 5rpx;
background: #b7b9b3;
width: 1rpx;
height: 70%;
opacity: 0.5;
}
}
.down-box{
display: flex;
flex-direction: row;
font-weight: 500;
height: 40rpx;
font-size: 22rpx;
color: #ACB1B5;
margin-top:10rpx;
.sex{
margin-left: auto ;
margin-right: 230rpx;
}
}
}
.up {
margin-top:50rpx;
width: 20rpx;
height: 20rpx;
opacity: 0.3;
transition: all ease 0.6s;
transform: rotate(0deg);
}
.down {
margin-top:50rpx;
width: 20rpx;
height: 20rpx;
opacity: 0.3;
transition: all ease 0.6s;
transform: rotate(-180deg);
}
}
.sub-box{
display: flex;
flex-direction: column;
background: #F6F6F6;
border-radius: 20rpx;
height: 120rpx;
margin-top:20rpx;
.up-box{
display: flex;
flex-direction: row;
align-items:flex-end;
height: 17rpx;
font-size: 22rpx;
font-family: Roboto;
font-weight: bold;
margin-top:36rpx;
margin-left: 35rpx;
.run-box{
margin-left:25rpx;
}
.divider{
margin-left:25rpx;
margin-bottom: 5rpx;
background: #b7b9b3;
width: 1rpx;
height: 100%;
opacity: 0.5;
}
}
.down-box{
display: flex;
flex-direction: row;
height: 18px;
margin-top:22rpx;
margin-left: 35rpx;
font-size: 18rpx;
font-weight: 500;
color: #000000;
opacity: 0.5;
}
}
}
.p_item {
overflow: hidden;
transition: max-height 0.4s ease;
}
参考文章
本实现方法主要参考了以下几个文章
https://blog.csdn.net/tieniuya/article/details/125769153
https://blog.csdn.net/wh20141212/article/details/107565986
https://blog.csdn.net/sriting/article/details/106111020