期望效果
实现思路
效果分析:蓝色区域(以下称为标题)不动,点击按钮,控制粉色区域(以下称为内容)的出现与消失。
思路1:针对“出现&消失”,首先可以考虑使用v-show,配合uni-app的内置组件transition来实现内容展示的过渡效果。但微信小程序不支持transition,所以放弃该方案。
思路2:虽然内置组件transition不能用,但是css里有个transition属性,它可以为元素设置过渡效果。transition: property duration timing-function delay; (这里用前两个就行)
思路分析:
确定使用css的transition的属性来实现内容的过渡效果,首先确定transition-property。因为内容消失的时候是向上平移,出现的时候是向下平移,所以可以使用margin-top。当内容出现时,其margin-top为0;当内容消失时,其margin-top为(-内容的高度)。(注意:property属性必须过渡前后都有确定的数值!否则过渡不生效)
标题在内容上方,为了不让内容上移时,遮挡住标题,可以为标题设置z-index。(注意:z-index属性只能在设置了position: relative | absolute | fixed的元素和父元素设置了 display: flex属性的子元素中起作用,在其他元素中是不作用的)
开始动手
展开组件制作
<template>
<view class="expand-box">
<view class="title-box">
<view class="title">
<slot name="title"></slot>
</view>
<view
class="icon"
:class="{ right: !expand }"
@click="handleExpand"
></view>
</view>
<view
id="content-box"
class="content-box"
:style="{
marginTop: expand ? 0 + 'px' : -contentHeight + 'px',
}"
>
<slot name="content"></slot>
</view>
</view>
</template>
<script setup>
import { ref, getCurrentInstance, onMounted, onBeforeUpdate } from 'vue'
const expand = ref(true)
const contentHeight = ref()
let query
onMounted(() => {
query = uni
.createSelectorQuery()
.in(getCurrentInstance())
.select('#content-box')
query
.boundingClientRect((data) => {
contentHeight.value = data.height
})
.exec()
})
onBeforeUpdate(() => {
query
.boundingClientRect((data) => {
contentHeight.value = data.height
})
.exec()
})
function handleExpand() {
expand.value = !expand.value
}
</script>
<style lang="scss" scoped>
.expand-box {
/* min-height的值必须有,且需与title-box的height保持一致。*/
/* 如果不设置min-height,当content-box的margin-top绝对值大于自身height值后,会影响expand-box高度*/
min-height: 80rpx;
overflow: hidden; /* 隐藏上移的内容 */
margin-bottom: 30rpx;
}
.title-box {
position: relative;
z-index: 1;
height: 80rpx;
background-color: skyblue;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.title {
font-weight: bold;
font-size: 30rpx;
}
.icon {
width: 40rpx;
height: 40rpx;
background-size: cover;
background-image: url('@/static/icons/expand.png');
transition: transform 0.8s;
}
.right {
transform: rotate(-90deg);
}
}
.content-box {
transition: margin-top 0.8s;
}
</style>
展开组件使用
// 组件需要绑定一个属性,属性名随意,但属性值一定要与content插槽内的内容数量有关
// 目的是为了告诉Expand组件内容发生了变化,需要重新获取内容的高度
<Expand :content="arr.length">
<template #title> 我是标题 </template>
<template #content>
<view v-for=“(item, index) in arr” :key="index">{{item}}</view>
</template>
</Expand>
<script setup>
import { ref } from 'vue'
const arr =[1,2,3]
</script>