uniapp微信小程序自定义手风琴组件


一、需求与背景

这边的需求是做一个手风琴样式的列表,需要有手风琴的动画效果,只能展开一个,列表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

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UniApp 是一个基于 Vue.js 的跨平台开发框架,可以用来开发微信小程序、H5、App等多个平台的应用。在 UniApp 中,可以通过自定义组件来实现对小程序的扩展和复用。 要创建一个自定义组件,可以按照以下步骤进行: 1. 在 UniApp 项目的 `components` 目录下创建一个新的文件夹,用于存放自定义组件的相关文件。 2. 在该文件夹下创建一个 `.vue` 文件,作为自定义组件的入口文件。 3. 在入口文件中定义组件的模板、样式和逻辑代码。 4. 在需要使用自定义组件的页面或组件中,引入并使用该自定义组件。 下面是一个简单的示例,演示如何创建一个自定义组件: 1. 在 `components` 目录下创建一个名为 `my-component` 的文件夹。 2. 在 `my-component` 文件夹下创建一个名为 `my-component.vue` 的文件,作为自定义组件的入口文件。 3. 在 `my-component.vue` 文件中编写如下代码: ```html <template> <view class="my-component"> <text>{{ message }}</text> </view> </template> <script> export default { name: 'MyComponent', props: { message: { type: String, default: '' } } } </script> <style scoped> .my-component { color: red; } </style> ``` 4. 在需要使用自定义组件的页面或组件中,引入并使用该自定义组件。例如,在某个页面的 `.vue` 文件中: ```html <template> <view> <my-component message="Hello World"></my-component> </view> </template> <script> import MyComponent from '@/components/my-component/my-component.vue' export default { components: { MyComponent } } </script> ``` 这样,就可以在页面中使用名为 `my-component` 的自定义组件了。 需要注意的是,自定义组件的文件夹和文件名可以根据实际情况进行命名,但要保持一致性。另外,自定义组件中的样式可以使用 `scoped` 关键字来实现样式的隔离,避免与其他组件的样式冲突。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值