在许多应用中,我们可能需要实现展开和收缩的动画效果。在本文中,我们将探讨如何在小程序中实现这种效果。本文只实现基础的展开/收缩动画组件,想要实现折叠面板,可以针对该组件做二次封装;支持嵌套折叠。
实现该功能分为下面两点:
- 实现节点高度逐渐变化的动画
- 节点高度可能不固定,需要动态获取节点高度
一、实现节点高度逐渐变化的动画
微信提供了动画api
wx.createAnimation(Object object)
创建一个动画实例。调用实例的方法来描述动画。最后通过动画实例的 export 方法导出动画数据传递给组件的 animation 属性。
<view animation="{{animationData}}"></view>
const animation = wx.createAnimation({
duration: 0,
timingFunction: 'ease-in-out'
});
animation
.height(400)
.top(1)
.step({
duration: 1500
})
.height('auto')
.step();
this.setData({
animationData: animation.export()
})
二、动态获取节点高度
微信提供了获取页面节点信息api
wx.createSelectorQuery()
wx.createSelectorQuery()
.select('#the-id')
.boundingClientRect(async res => {
console.log('res====', res);
})
.exec();
三、完整代码
<view class='transition-collapse' animation='{{ animationData }}'>
<view class='transition-collapse__content'>
<slot></slot>
</view>
</view>
Component({
options: {
pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段
},
properties: {
// 是否折叠
collapsed: {
type: Boolean,
value: false,
observer() {
if (!this.data._mounted) return
this.updateAnimate()
}
},
// 动画时间
duration: {
type: Number,
value: 300
},
// 动画时间曲线
timingFunction: {
type: 'String',
value: 'ease-in-out'
}
},
data: {
animationData: null,
// 是否已渲染,首次渲染时,如果是展开状态则不需要动画
_mounted: false
},
attached() {
this.updateAnimate()
this.setData({
_mounted: true
})
},
methods: {
// 执行动画
async updateAnimate() {
const { _mounted, collapsed, duration, timingFunction } = this.data
const height = await this.getNodeRect();
const animation = wx.createAnimation({
duration: 0,
timingFunction
});
if (collapsed) {
// 增加容错,防止查询节点尺寸失败
if (height === 0) {
animation.height('auto').top(1).step();
} else {
animation
.height(height)
.top(1)
.step({
duration: _mounted ? duration : 1
})
.height('auto')
.step();
}
this.setData({
animationData: animation.export()
});
return
}
if (_mounted) {
animation.height(height).top(0).step({ duration: 1 }).height(0).step({
duration
});
}
this.setData({
animationData: animation.export()
});
},
// 查询节点尺寸
getNodeRect(selector = '.transition-collapse__content') {
return new Promise((resolve) => {
wx.createSelectorQuery()
.in(this)
.select(selector)
.boundingClientRect(res => {
if (res?.height) {
return resolve(res.height);
}
return resolve(0);
})
.exec();
});
}
}
})
.transition-collapse {
overflow: hidden;
height: 0;
}
.transition-collapse__content {
overflow: hidden;
}
四、使用
<w-transition-collapse collapsed="{{collapsed}}">
<view wx:for="{{[1, 2, 3, 4, 5, 6, 7]}}" wx:key="index">折叠内容{{item}}</view>
</w-transition-collapse>
<button bindtap="handleClick">{{collapsed ? '折叠' : '展开'}}</button>
Page({
data: {
collapsed: false
},
handleClick() {
this.setData({
collapsed: !this.data.collapsed
})
}
})
{
"usingComponents": {
"w-transition-collapse": "../../components/transition-collapse/index"
}
}
示例使用的
微信平台,其他平台使用对应的api即可
五、组件支持度
| 小程序平台 | 支持度 |
|---|---|
| 微信 | 支持 |
| 百度 | 支持 |
| 京东 | 支持 |
| 小红书 | 支持 |