You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native聊天应用自定义音频气泡实现方法咨询(View尝试未果)

React Native 自定义音频消息气泡实现方案

下面是针对你需求的具体实现步骤和代码,完全匹配你提供的参考样式:

1. 依赖准备

首先安装需要的第三方库:

npm install react-native-linear-gradient
# iOS 额外执行
cd ios && pod install && cd ..

这个库用来实现气泡的渐变背景,如果你不需要渐变,也可以直接用普通View的backgroundColor替代。

2. 完整组件代码

import React, { useState, useRef, useEffect } from 'react';
import { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';

const AudioMessageBubble = ({ isMe, duration, onPressPlay, isPlaying }) => {
  // 波形动画控制
  const bar1Anim = useRef(new Animated.Value(1)).current;
  const bar2Anim = useRef(new Animated.Value(0.6)).current;
  const bar3Anim = useRef(new Animated.Value(0.8)).current;

  useEffect(() => {
    if (isPlaying) {
      // 播放时循环动画
      const animateWave = () => {
        Animated.sequence([
          Animated.parallel([
            Animated.timing(bar1Anim, { toValue: 0.6, duration: 300, useNativeDriver: false }),
            Animated.timing(bar2Anim, { toValue: 1, duration: 300, useNativeDriver: false }),
            Animated.timing(bar3Anim, { toValue: 0.6, duration: 300, useNativeDriver: false }),
          ]),
          Animated.parallel([
            Animated.timing(bar1Anim, { toValue: 0.8, duration: 300, useNativeDriver: false }),
            Animated.timing(bar2Anim, { toValue: 0.6, duration: 300, useNativeDriver: false }),
            Animated.timing(bar3Anim, { toValue: 1, duration: 300, useNativeDriver: false }),
          ]),
        ]).start(() => animateWave());
      };
      animateWave();
    } else {
      // 暂停时重置波形
      Animated.parallel([
        Animated.timing(bar1Anim, { toValue: 1, duration: 200, useNativeDriver: false }),
        Animated.timing(bar2Anim, { toValue: 0.6, duration: 200, useNativeDriver: false }),
        Animated.timing(bar3Anim, { toValue: 0.8, duration: 200, useNativeDriver: false }),
      ]).start();
    }
  }, [isPlaying]);

  return (
    <View style={[styles.container, isMe ? styles.myMsgWrapper : styles.otherMsgWrapper]}>
      {/* 气泡主体 */}
      <LinearGradient
        colors={isMe ? ['#6366F1', '#8B5CF6'] : ['#E5E7EB', '#D1D5DB']}
        style={[styles.bubble, isMe ? styles.myBubble : styles.otherBubble]}
      >
        <TouchableOpacity onPress={onPressPlay} style={styles.playBtn}>
          <Text style={styles.playIcon}>{isPlaying ? '⏸' : '▶'}</Text>
        </TouchableOpacity>
        {/* 动态波形 */}
        <View style={styles.waveContainer}>
          <Animated.View style={[styles.waveBar, { transform: [{ scaleY: bar1Anim }] }]} />
          <Animated.View style={[styles.waveBar, { transform: [{ scaleY: bar2Anim }] }]} />
          <Animated.View style={[styles.waveBar, { transform: [{ scaleY: bar3Anim }] }]} />
        </View>
        {/* 时长文本 */}
        <Text style={[styles.durationTxt, isMe ? styles.myTxt : styles.otherTxt]}>
          {duration}''
        </Text>
      </LinearGradient>
      {/* 气泡三角箭头 */}
      <View style={[styles.triangle, isMe ? styles.myTriangle : styles.otherTriangle]} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    marginVertical: 8,
    marginHorizontal: 12,
    alignItems: 'center',
  },
  myMsgWrapper: {
    justifyContent: 'flex-end',
  },
  otherMsgWrapper: {
    justifyContent: 'flex-start',
  },
  bubble: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 10,
    paddingHorizontal: 16,
    borderRadius: 20,
    minWidth: 120,
  },
  myBubble: {
    borderTopRightRadius: 8, // 自己发的气泡右上角改成小圆角
  },
  otherBubble: {
    borderTopLeftRadius: 8, // 对方发的气泡左上角改成小圆角
  },
  playBtn: {
    marginRight: 12,
  },
  playIcon: {
    fontSize: 20,
    color: '#fff',
  },
  waveContainer: {
    flexDirection: 'row',
    alignItems: 'flex-end',
    height: 24,
    flex: 1,
    justifyContent: 'center',
    marginRight: 12,
  },
  waveBar: {
    width: 4,
    backgroundColor: '#fff',
    marginHorizontal: 2,
    borderRadius: 2,
  },
  durationTxt: {
    fontSize: 14,
  },
  myTxt: {
    color: '#fff',
  },
  otherTxt: {
    color: '#374151',
  },
  triangle: {
    width: 0,
    height: 0,
    position: 'absolute',
    top: 10,
  },
  myTriangle: {
    right: -8,
    borderTopWidth: 8,
    borderLeftWidth: 8,
    borderBottomWidth: 8,
    borderLeftColor: '#6366F1',
    borderTopColor: 'transparent',
    borderBottomColor: 'transparent',
  },
  otherTriangle: {
    left: -8,
    borderTopWidth: 8,
    borderRightWidth: 8,
    borderBottomWidth: 8,
    borderRightColor: '#E5E7EB',
    borderTopColor: 'transparent',
    borderBottomColor: 'transparent',
  },
});

export default AudioMessageBubble;

3. 使用方式

在聊天列表中直接引入组件,根据消息发送者传入对应参数:

// 自己发送的音频消息
<AudioMessageBubble
  isMe={true}
  duration={15}
  onPressPlay={() => { /* 播放音频的逻辑 */ }}
  isPlaying={false}
/>

// 对方发送的音频消息
<AudioMessageBubble
  isMe={false}
  duration={22}
  onPressPlay={() => { /* 播放音频的逻辑 */ }}
  isPlaying={false}
/>

4. 自定义调整建议

  • 颜色调整:修改LinearGradient的colors数组,替换成你需要的渐变配色;三角箭头的颜色要和渐变边缘色保持一致。
  • 波形优化:如果需要更细腻的波形,可以用react-native-svg绘制SVG波形,结合Animated实现更复杂的动画效果。
  • 图标替换:把示例中的文本图标(▶/⏸)换成你项目中的图标组件(比如react-native-vector-icons)。

内容的提问来源于stack exchange,提问作者Riaz Yousaf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 10:52:23