index.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import IconMutedVoice from "@/images/icon-muted-voice.png";
  2. import IconPlay from "@/images/icon-play.png";
  3. import IconVoiceWave from "@/images/icon-voice-wave.png";
  4. import { Image, View } from "@tarojs/components";
  5. import Taro, { useUnload } from "@tarojs/taro";
  6. import { forwardRef, useImperativeHandle, useState } from "react";
  7. import style from "./index.module.less";
  8. import { TVoiceItem } from "@/types/voice";
  9. interface Props {
  10. voiceItem?: TVoiceItem | null
  11. }
  12. export interface IVoicePlayerBar {
  13. play: (voice: string) => Promise<void>;
  14. stop: () => void;
  15. }
  16. const audioInstance = Taro.createInnerAudioContext();
  17. export default forwardRef<IVoicePlayerBar, Props>(({voiceItem}:Props, ref)=> {
  18. const [playing, setPlaying] = useState(false);
  19. audioInstance.onEnded(()=> {
  20. setPlaying(false)
  21. })
  22. const stopAudio = ()=> {
  23. audioInstance.stop()
  24. setPlaying(false)
  25. }
  26. const playVoice = async (voiceUrl: string) => {
  27. stopAudio();
  28. try{
  29. audioInstance.src = voiceUrl
  30. audioInstance.play()
  31. setPlaying(true)
  32. }catch(e){
  33. setPlaying(false)
  34. }
  35. }
  36. const handlePlay = (voice: string)=> {
  37. if(!playing && voice){
  38. playVoice(voice)
  39. setPlaying(true)
  40. }
  41. }
  42. useImperativeHandle(ref, () => {
  43. return {
  44. play: playVoice,
  45. stop: stopAudio,
  46. };
  47. });
  48. useUnload(()=> {
  49. stopAudio();
  50. })
  51. const renderPlayerBar = () => {
  52. if (voiceItem) {
  53. return (
  54. <>
  55. <View className={style.playStatus} onClick={()=> handlePlay(voiceItem.voiceUrl)}>
  56. <Image
  57. src={playing ? IconVoiceWave : IconPlay}
  58. mode='widthFix'
  59. className={style.icon}
  60. ></Image>
  61. </View>
  62. <View className='text-14 leading-22 font-medium truncate'>
  63. {voiceItem?.voiceName}
  64. {/* {voiceItem?.gender === 'male' ? "男" : "女"} */}
  65. </View>
  66. </>
  67. );
  68. }
  69. // 没有音色的状态
  70. return (
  71. <>
  72. <View className={style.playStatus}>
  73. <Image
  74. src={IconMutedVoice}
  75. mode='widthFix'
  76. className={style.icon}
  77. ></Image>
  78. </View>
  79. <View className='text-14 leading-22 font-medium text-gray-4'>
  80. 请选择声音
  81. </View>
  82. </>
  83. );
  84. };
  85. return <>
  86. <View className={style.playerBar}>{renderPlayerBar()}</View>
  87. </>
  88. })