vue3+uniapp点击切换动态播放语音效果动画:模仿微信语音播放动画效果,可以分别显示成不同颜色等!已经封装成组件了!

效果:

组件:

<!--聊天页面-播放语言组件-->
<template>
  <view :class="['voice-message', { 'sent': !isSent, 'received': isSent }]"

        @click="togglePlayback">
<!--    默认情况下的样式,属于是接收者看到的样式,会显示在聊天框的最左侧。如果isSents true的时候,需要把【voice-icon】里面的整体翻转过来显示-->
    <!-- 最大宽度为147px {60:294 === 10:49} -->
<!--    语言条要按照语音时长显示不同的宽度,所以增加了一块宽度,发送者的时候,加在左侧,接收者的时候,加在右侧-->
    <view v-if="isSent" :style="`width:${duration / 10 * 49}rpx`"></view>
    <text class="duration" v-if="isSent">{{ duration }}''&nbsp;</text>
    <view :class="['voice-icon', { 'sent-icon': isSent }]">
      <view :class="['small']" :style="smallStyle"></view>
      <view :class="['middle', { 'animate': isPlaying }]" :style="middleStyle"></view>
      <view :class="['large', { 'animate': isPlaying }]" :style="largeStyle"></view>
    </view>
    <text class="duration" :style="{color:iconColor}" v-if="!isSent">{{ duration }}&nbsp;''</text>
	<!-- 最大宽度为147px {60:294 === 10:49} -->
	<view v-if="!isSent" :style="`width:${duration / 10 * 49}rpx`"></view>
  </view>
</template>

<script setup>
import { ref, watch, computed,onUnmounted,defineEmits  } from 'vue';

// 从 props 中获取动态参数
const props = defineProps({
  isSent: {
    type: Boolean,//是不是发送者?两者是对称反转的样式
    default: false
  },
  iconColor: {
    type: String,
    default: '#FFFFFF'
  },
  smallSize: {
    type: Number,
    default: 10
  },
  middleSize: {
    type: Number,
    default: 20
  },
  largeSize: {
    type: Number,
    default: 30
  },
  duration: {
    type: String,
    default: '00'
  },
  audioSrc: {
    type: String,
    default: ''
  }
});

const isPlaying = ref(false);
let innerAudioContext = null;
// 定义 emit 事件
const emit = defineEmits(['update:playStatus']);



// 计算动态样式
const smallStyle = computed(() => ({
  color: props.iconColor,
  width: `${props.smallSize}rpx`,
  height: `${props.smallSize}rpx`,
  marginRight: '-10rpx' // 调整间距
}));

const middleStyle = computed(() => ({
  color: props.iconColor,
  width: `${props.middleSize}rpx`,
  height: `${props.middleSize}rpx`,
  marginRight: '-30rpx' // 调整间距
}));

const largeStyle = computed(() => ({
  color: props.iconColor,
  width: `${props.largeSize}rpx`,
  height: `${props.largeSize}rpx`,
  marginRight: '1rpx' // 调整间距
}));


// 切换播放状态的函数
function togglePlayback() {
  if (isPlaying.value) {
    pauseVoice();
  } else {
    playVoice(props.audioSrc);
  }
  isPlaying.value = !isPlaying.value;
  emit('update:playStatus', isPlaying.value); // 触发事件,返回当前播放状态
}

// 播放音频的函数
function playVoice(voiceSrc) {
  console.log('播放录音', voiceSrc);

  // 如果音频上下文不存在,则创建新的音频上下文
  if (!innerAudioContext) {
    innerAudioContext = uni.createInnerAudioContext();
  }

  // 设置音频源
  innerAudioContext.src = voiceSrc;

  // 播放音频
  innerAudioContext.play();

  // 监听播放结束事件
  innerAudioContext.onEnded(() => {
    isPlaying.value = false;
  });
}

// 暂停音频的函数
function pauseVoice() {
  if (innerAudioContext) {
    innerAudioContext.pause();
  }
}

// 组件卸载时销毁音频上下文
onUnmounted(() => {
  if (innerAudioContext) {
    innerAudioContext.destroy();
    innerAudioContext = null;
  }
});
</script>

<style scoped>
.voice-message {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.voice-message.sent {
  justify-content: flex-end;
}

.voice-message.received {
  justify-content: flex-start;
}

.voice-icon {
  display: flex;
  align-items: center;
}

.voice-icon.sent-icon {
  transform: rotate(180deg);
}

.small, .middle, .large {
  border-style: solid;
  border-top-color: transparent;
  border-left-color: transparent;
  border-bottom-color: transparent;
  border-radius: 50%;
  box-sizing: border-box;
  vertical-align: middle;
  display: inline-block;
  background-color: transparent; /* 默认背景颜色为透明 */
}

.middle.animate {
  animation: show2 3s ease-in-out infinite;
}

.large.animate {
  animation: show3 3s ease-in-out infinite;
}

@keyframes show2 {
  0% { opacity: 0; }
  30% { opacity: 1; }
  100% { opacity: 0; }
}

@keyframes show3 {
  0% { opacity: 0; }
  60% { opacity: 1; }
  100% { opacity: 0; }
}

 

.duration {
  margin-left: 8px;
  font-size: 32rpx;
  color: #FFFFFF;
  font-weight: 400;
}
</style>

使用组件:

  <VoicePlayer
                            :isSent="false"
                            iconColor="#333333"
                            :smallSize="5"
                            :middleSize="30"
                            :largeSize="48"
                            :duration="item.voice_time"
                            :audioSrc="item.voice"
                            v-model:playStatus="playStatus[item.message_id]"
                            @update:playStatus="handlePlayStatusChange(item.message_id, $event)"
                        />

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue3+uni-app实现微信小程序登录流程主要分为以下步骤: 1. 在uni-app项目中安装并引入微信小程序登录插件(如wxlogin),在main.js中初始化并配置插件参数。 2. 创建一个登录页面,该页面包含点击按钮触发微信登录的操作,可以通过uni.login()方法调用微信小程序登录接口获取code。 3. 接收到微信小程序登录接口返回的code后,将code发送给后端服务器,后端服务器将code和小程序的App ID以及App Secret发送给微信服务器进行登录凭证校验,获取到session_key和openid。 4. 服务器根据openid和session_key生一个自定义的token,返回给前端。 5. 前端将token保存在本地,使用uni.setStorage()方法进行存储,以便后续的登录状态维持和接口请求验证。 6. 在需要登录验证的页面或组件中,通过uni.getStorage()方法获取本地存储的token,并将token添加到请求头中,发送给后端服务器进行接口请求。 7. 后端服务器接收到带有token的请求,对token进行校验和解析,验证token是否有效,从而确保用户的登录状态。 总结:通过以上步骤,实现了Vue3+uni-app微信小程序的登录流程。用户通过点击按钮触发微信小程序登录接口,后端服务器校验登录凭证,生token并返回给前端,前端保存token并在请求接口时携带token进行验证,保证了用户的登录状态和接口访问权限的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南北极之间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值