129 lines
2.6 KiB
Vue
129 lines
2.6 KiB
Vue
<template>
|
||
<view class="recording-wave-btn">
|
||
<view class="audio-visualizer">
|
||
<view
|
||
v-for="(bar, index) in audioBars"
|
||
:key="index"
|
||
class="audio-bar"
|
||
:style="{
|
||
height: bar.height + 'px',
|
||
transition: 'height 0.1s ease-out',
|
||
}"
|
||
></view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted } from "vue";
|
||
|
||
const animationTimer = ref(null);
|
||
const isAnimating = ref(false);
|
||
|
||
// 音频条数据
|
||
const audioBars = ref([]);
|
||
const BAR_COUNT = 30;
|
||
|
||
// 初始化音频条
|
||
const initAudioBars = () => {
|
||
audioBars.value = [];
|
||
for (let i = 0; i < BAR_COUNT; i++) {
|
||
audioBars.value.push({
|
||
height: 4 + Math.random() * 8,
|
||
});
|
||
}
|
||
};
|
||
|
||
// 更新音频条动画
|
||
const updateAudioBars = () => {
|
||
if (!isAnimating.value) return;
|
||
|
||
// 使用 for 循环随机修改每个音频条的高度
|
||
for (let i = 0; i < audioBars.value.length; i++) {
|
||
const bar = audioBars.value[i];
|
||
|
||
// 生成随机高度值
|
||
const minHeight = 4;
|
||
const maxHeight = 20;
|
||
const randomHeight = minHeight + Math.random() * (maxHeight - minHeight);
|
||
|
||
// 添加一些变化幅度,让高度变化更自然
|
||
const variation = (Math.random() - 0.5) * 10;
|
||
bar.height = Math.max(
|
||
minHeight,
|
||
Math.min(maxHeight, randomHeight + variation)
|
||
);
|
||
}
|
||
};
|
||
|
||
// 开始动画
|
||
const startAnimation = () => {
|
||
if (!isAnimating.value) {
|
||
isAnimating.value = true;
|
||
|
||
const animate = () => {
|
||
if (isAnimating.value) {
|
||
updateAudioBars();
|
||
animationTimer.value = setTimeout(animate, 200); // 每200ms更新一次,模仿人类说话语速
|
||
}
|
||
};
|
||
|
||
animate();
|
||
}
|
||
};
|
||
|
||
// 停止动画
|
||
const stopAnimation = () => {
|
||
isAnimating.value = false;
|
||
if (animationTimer.value) {
|
||
clearTimeout(animationTimer.value);
|
||
animationTimer.value = null;
|
||
}
|
||
};
|
||
|
||
// 组件挂载时初始化并自动开始动画
|
||
onMounted(() => {
|
||
initAudioBars();
|
||
startAnimation();
|
||
});
|
||
|
||
// 组件卸载时停止动画
|
||
onUnmounted(() => {
|
||
stopAnimation();
|
||
});
|
||
|
||
// 暴露方法给父组件
|
||
defineExpose({
|
||
startAnimation,
|
||
stopAnimation,
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.recording-wave-btn {
|
||
box-shadow: 0px 0px 20px 0px rgba(52, 25, 204, 0.05);
|
||
margin: 0 12px;
|
||
margin-bottom: 8px;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background-color: #00a6ff;
|
||
height: 44px;
|
||
border-radius: 50px;
|
||
}
|
||
|
||
.audio-visualizer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 20px;
|
||
gap: 2px;
|
||
}
|
||
|
||
.audio-bar {
|
||
width: 2px;
|
||
height: 4px;
|
||
border-radius: 2px;
|
||
background-color: #fff;
|
||
}
|
||
</style> |