开发中,动画是提升用户体验的关键要素。本文将深入探讨我在 Taro+React 项目中实现的两种动画效果:麦克风录音的呼吸动画和语音消息的波形动画,分享技术实现细节和设计思考。
1. 麦克风呼吸动画:录音状态的可视化反馈
场景需求
当用户开始录音时,我们需要通过视觉反馈让用户明确知道系统正在收音。这种反馈需要:
- 直观表达"正在录音"的状态
- 保持愉悦的用户体验
- 不过度消耗系统资源
实现方案
javascript
import Taro, { useRef, useEffect } from '@tarojs/taro'
const RecordButton = ({ isRecording }) => {
const animationRef = useRef(null)
// 处理录音动画效果useEffect(() => {
if (!isRecording) return;
const createAnimation = () => {
// 创建基础动画实例const animation = Taro.createAnimation({
duration: 800,// 动画持续时间timingFunction: 'ease-in-out'// 缓动函数
})
// 定义动画关键帧
animation.scale(1.1).step()// 第一步:放大
animation.scale(1.0).step()// 第二步:恢复
return animation;
}
// 递归动画函数const animate = () => {
if (!isRecording) return;
const animation = createAnimation()
animationRef.current = animation
// 使用requestAnimationFrame确保动画流畅requestAnimationFrame(() => {
animation.export()
})
// 递归调用形成循环setTimeout(animate, 1600)// 总动画周期1.6秒
}
// 启动动画animate();
// 清理函数return () => {
animationRef.current = null;
}
}, [isRecording])
return (
<View
className={`audio-icon ${isRecording ? 'recording' : ''}`}
animation={animationRef.current}
>
<Image src="/assets/microphone.png" />
</View>
)
}
对应CSS:
css
.audio-icon {
margin: auto;
width: 140px;
height: 140px;
transition: transform 0.3s ease-in-out;
background-size: contain;
// 使用CSS动画作为备选方案
&.recording {
animation: pulse 1.6s infinite ease-in-out;
}
}
// 呼吸动画关键帧@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
技术亮点
-
双方案实现:
- 首选Taro的API驱动动画
- CSS动画作为优雅降级方案
-
动画递归调用:
javascriptconst animate = () => { // 创建动画...setTimeout(animate, 1600) } animate()
这种递归模式比setInterval更可控
-
性能优化:
- 使用requestAnimationFrame确保流畅性
- 清理阶段解除引用避免内存泄漏
-
参数调优:
- 1.1倍的缩放比例提供足够视觉变化,但不过分夸张
- 1.6秒周期创造自然的呼吸节奏
2. 语音波形动画:模拟音浪跳动
场景需求
在语音消息气泡中,需要通过动态效果表达:
- 消息类型是语音而非文本
- 即使不播放也暗示其动态属性
- 创建视觉吸引力引导点击
实现方案
javascript
const AudioMessage = () => (
<View className="audio-bubble">
<View className="audio-wave">
{[1, 2, 3, 4, 5].map(index => (
<View
key={index}
className="wave-bar"
style={{
animationDelay: `${(index-1) * 0.2}s`,
height: '50%'
}}
/>
))}
</View>
<Text>2:36</Text> {/* 语音时长 */}
</View>
)
对应CSS:
css
.audio-bubble {
display: flex;
align-items: center;
padding: 10rpx 20rpx;
// 不同用户类型的样式区分.current-user & {
background-color: #daf0ff;
}
.other-user & {
background-color: #f0f2f5;
}
}
.audio-wave {
display: flex;
align-items: center;
height: 40rpx;
margin-right: 15rpx;
.wave-bar {
width: 8rpx;
height: 30rpx;
background-color: #1890ff;
margin: 0 4rpx;
border-radius: 4rpx;
animation: wave 1.2s infinite ease-in-out;
// 为每个波形条设置不同的延迟
&:nth-child(1) { animation-delay: 0s; }
&:nth-child(2) { animation-delay: 0.2s; }
&:nth-child(3) { animation-delay: 0.4s; }
&:nth-child(4) { animation-delay: 0.6s; }
&:nth-child(5) { animation-delay: 0.8s; }
// 不同用户颜色设置.current-user & {
background-color: white;
}
}
}
@keyframes wave {
0%, 100% {
height: 10rpx;
}
50% {
height: 30rpx;
}
}
技术亮点
-
分布式延迟设计:
css&:nth-child(1) { animation-delay: 0s; } &:nth-child(2) { animation-delay: 0.2s; } ...
每个波形柱延迟依次增加0.2秒,创造波浪效果
-
振幅控制:
css@keyframes wave { 0%, 100% { height: 10rpx; } 50% { height: 30rpx; } }
中点为最高值,创造真实波形运动感
-
动态高度适应:
通过设置起始和结束点为小振幅,中点为高峰值:
- 更符合声波物理特性
- 相比0到100%的线性变化更自然
-
上下文感知颜色:
css.current-user .wave-bar { background-color: white; }
根据消息来源自动调整颜色,保持设计一致性
动画设计原则
1. 性能优先策略
- 硬件加速:使用transform而非height/width
- 层控制:限制will-change使用范围
- 平衡刷新率:60fps是最佳目标(16.67ms/帧)
2. 用户体验考虑
麦克风动画:
- 持续反馈:明确录音状态
- 中断复位:停止录音立即回到初始状态
- 心理模型契合:呼吸模式暗示"活着的"录音状态
波形动画:
- 隐含功能暗示:动态效果暗示点击播放
- 无声音的预览:即使静音状态也能感知消息类型
- 节奏引导:波浪方向暗示时间流动