From 0a02df0176e3d0deed6790ab51c623fe6439b344 Mon Sep 17 00:00:00 2001 From: xwenliang Date: Mon, 8 Dec 2025 04:33:01 +0800 Subject: [PATCH] feat: playing ui --- components/audio-overlay/audio-overlay.js | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/audio-overlay/audio-overlay.json | 4 ++++ components/audio-overlay/audio-overlay.wxml | 21 +++++++++++++++++++++ components/audio-overlay/audio-overlay.wxss | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pages/index/index.js | 31 ++++++++++++++++++++++++++++++- pages/index/index.json | 6 +++++- pages/index/index.wxml | 8 ++++++++ pages/result/result.js | 29 ++++++++++++++++++++++++++++- pages/result/result.json | 6 +++++- pages/result/result.wxml | 9 ++++++++- 10 files changed, 340 insertions(+), 5 deletions(-) create mode 100644 components/audio-overlay/audio-overlay.js create mode 100644 components/audio-overlay/audio-overlay.json create mode 100644 components/audio-overlay/audio-overlay.wxml create mode 100644 components/audio-overlay/audio-overlay.wxss diff --git a/components/audio-overlay/audio-overlay.js b/components/audio-overlay/audio-overlay.js new file mode 100644 index 0000000..c24e83b --- /dev/null +++ b/components/audio-overlay/audio-overlay.js @@ -0,0 +1,114 @@ +// 音频播放蒙层组件 +Component({ + properties: { + // 是否显示蒙层 + isPlaying: { + type: Boolean, + value: false + }, + // 播放的文本内容 + audioText: { + type: String, + value: '' + } + }, + + data: { + progress: 0, // 播放进度百分比 + currentTime: '00:00', // 当前播放时间 + duration: '00:00', // 总时长 + timer: null // 定时器 + }, + + lifetimes: { + detached() { + // 组件销毁时清理定时器 + this.clearTimer(); + } + }, + + methods: { + // 初始化音频监听 + initAudio(audioContext) { + if (!audioContext) return; + + this.audioContext = audioContext; + + // 监听音频播放进度 + audioContext.onTimeUpdate(() => { + const currentTime = audioContext.currentTime || 0; + const duration = audioContext.duration || 0; + + this.setData({ + progress: duration > 0 ? (currentTime / duration) * 100 : 0, + currentTime: this.formatTime(currentTime), + duration: this.formatTime(duration) + }); + }); + + // 监听播放结束 + audioContext.onEnded(() => { + this.stopPlaying(); + }); + + // 监听播放错误 + audioContext.onError(() => { + this.stopPlaying(); + }); + + // 监听播放停止 + audioContext.onStop(() => { + this.stopPlaying(); + }); + }, + + // 格式化时间显示 + formatTime(seconds) { + if (!seconds || isNaN(seconds)) return '00:00'; + + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + }, + + // 停止播放 + stopPlaying() { + this.setData({ + progress: 0, + currentTime: '00:00', + duration: '00:00' + }); + + this.clearTimer(); + + // 触发父组件的停止事件 + this.triggerEvent('stop'); + }, + + // 清理定时器 + clearTimer() { + if (this.data.timer) { + clearInterval(this.data.timer); + this.setData({ + timer: null + }); + } + }, + + // 点击蒙层背景 + onOverlayTap() { + // 点击背景关闭蒙层并停止播放 + this.stopPlaying(); + }, + + // 点击停止按钮 + onStopTap() { + this.stopPlaying(); + }, + + // 阻止事件冒泡 + stopPropagation() { + // 阻止点击内容区域时触发背景点击事件 + } + } +}); \ No newline at end of file diff --git a/components/audio-overlay/audio-overlay.json b/components/audio-overlay/audio-overlay.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/components/audio-overlay/audio-overlay.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/components/audio-overlay/audio-overlay.wxml b/components/audio-overlay/audio-overlay.wxml new file mode 100644 index 0000000..d99655c --- /dev/null +++ b/components/audio-overlay/audio-overlay.wxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + {{audioText}} + + + + + {{currentTime}} / {{duration}} + + + + \ No newline at end of file diff --git a/components/audio-overlay/audio-overlay.wxss b/components/audio-overlay/audio-overlay.wxss new file mode 100644 index 0000000..7f413c2 --- /dev/null +++ b/components/audio-overlay/audio-overlay.wxss @@ -0,0 +1,117 @@ +/* 音频播放蒙层样式 */ +.audio-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.overlay-content { + background: white; + border-radius: 16rpx; + padding: 60rpx 40rpx; + margin: 40rpx; + text-align: center; + min-width: 500rpx; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.2); +} + +.audio-icon { + margin-bottom: 40rpx; +} + +.sound-wave { + display: flex; + align-items: center; + justify-content: center; + gap: 8rpx; + height: 80rpx; +} + +.wave { + width: 6rpx; + background: #1296db; + border-radius: 3rpx; + animation: wave 1.5s ease-in-out infinite; +} + +.wave:nth-child(1) { + height: 20rpx; + animation-delay: 0s; +} + +.wave:nth-child(2) { + height: 40rpx; + animation-delay: 0.1s; +} + +.wave:nth-child(3) { + height: 60rpx; + animation-delay: 0.2s; +} + +.wave:nth-child(4) { + height: 40rpx; + animation-delay: 0.3s; +} + +@keyframes wave { + 0%, 100% { + transform: scaleY(1); + } + 50% { + transform: scaleY(1.5); + } +} + +.audio-text { + font-size: 32rpx; + color: #333; + margin-bottom: 40rpx; + line-height: 1.4; + max-height: 200rpx; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} + +.progress-container { + margin-bottom: 40rpx; +} + +.progress-bar { + width: 100%; + height: 8rpx; + background: #f0f0f0; + border-radius: 4rpx; + overflow: hidden; + margin-bottom: 16rpx; +} + +.progress-fill { + height: 100%; + background: #1296db; + transition: width 0.3s ease; +} + +.time-info { + font-size: 24rpx; + color: #666; +} + +.stop-btn { + background: #ff4757; + color: white; + border: none; + border-radius: 50rpx; + padding: 20rpx 60rpx; + font-size: 30rpx; +} \ No newline at end of file diff --git a/pages/index/index.js b/pages/index/index.js index aa50d1a..59c4c69 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -12,7 +12,9 @@ Page({ placeholder: '点击此处输入文字', text: '', typeList, - selectedIndex: String(selectedIndex) ? selectedIndex : 0 + selectedIndex: String(selectedIndex) ? selectedIndex : 0, + isAudioPlaying: false, + currentAudioText: '' }, onLoad(){ wx.showShareMenu(); @@ -141,6 +143,20 @@ Page({ audio = wx.createInnerAudioContext({useWebAudioImplement: true}); audio.autoplay = true; + // 显示播放蒙层 + this.setData({ + isAudioPlaying: true, + currentAudioText: text + }); + + // 初始化蒙层组件的音频监听 + setTimeout(() => { + const overlayComponent = this.selectComponent('audio-overlay'); + if (overlayComponent) { + overlayComponent.initAudio(audio); + } + }, 100); + // 检查是否播放过 const audioList = app.globalData.getAudioList(); let played = audioList.find(v => v.text === text && v.type === type); @@ -153,6 +169,7 @@ Page({ // 播放失败要移除缓存内容 audio.onError(err => { app.globalData.setAudioList('delete', played.path); + this.setData({ isAudioPlaying: false }); wx.showModal({ title: '提示', content: '该语音播放过但缓存失效,点击确定重新播放', @@ -189,6 +206,7 @@ Page({ // 不合规结束 if(result.suggest !== 'pass'){ wx.hideLoading(); + this.setData({ isAudioPlaying: false }); return wx.showToast({ title: '内容不合规', icon: 'error', @@ -221,6 +239,7 @@ Page({ fail(err) { console.log(err); wx.hideLoading(); + this.setData({ isAudioPlaying: false }); wx.showToast({ title: '下载失败', icon: 'none' @@ -229,6 +248,7 @@ Page({ }); } catch (error) { wx.hideLoading(); + this.setData({ isAudioPlaying: false }); // TTS接口调用失败的错误已经在app.js中处理了 } }, @@ -243,6 +263,15 @@ Page({ audio.destroy(); audio = null; } + this.setData({ + isAudioPlaying: false, + currentAudioText: '' + }); + }, + + // 音频停止播放的回调 + onAudioStop() { + this.resetAudio(); }, selectIndex(e){ const { index } = e.target.dataset; diff --git a/pages/index/index.json b/pages/index/index.json index 9e26dfe..e273712 100644 --- a/pages/index/index.json +++ b/pages/index/index.json @@ -1 +1,5 @@ -{} \ No newline at end of file +{ + "usingComponents": { + "audio-overlay": "../../components/audio-overlay/audio-overlay" + } +} \ No newline at end of file diff --git a/pages/index/index.wxml b/pages/index/index.wxml index f005480..3c364f9 100644 --- a/pages/index/index.wxml +++ b/pages/index/index.wxml @@ -28,5 +28,13 @@ > {{item.desc}} + + + + \ No newline at end of file diff --git a/pages/result/result.js b/pages/result/result.js index aabb334..3e298fd 100644 --- a/pages/result/result.js +++ b/pages/result/result.js @@ -16,7 +16,9 @@ let formateDate = function (audioList) { Page({ data: { - audioList: formateDate(app.globalData.getAudioList()) + audioList: formateDate(app.globalData.getAudioList()), + isAudioPlaying: false, + currentAudioText: '' }, onLoad(options){ this.setData({ @@ -33,8 +35,24 @@ Page({ audio = wx.createInnerAudioContext({useWebAudioImplement: true}); audio.autoplay = true; audio.src = target.path; + + // 显示播放蒙层 + this.setData({ + isAudioPlaying: true, + currentAudioText: target.text + }); + + // 初始化蒙层组件的音频监听 + setTimeout(() => { + const overlayComponent = this.selectComponent('audio-overlay'); + if (overlayComponent) { + overlayComponent.initAudio(audio); + } + }, 100); + // 播放失败要移除缓存内容 audio.onError(err => { + this.setData({ isAudioPlaying: false }); wx.showModal({ title: '提示', content: '该语音缓存失效,点击确定删除', @@ -80,5 +98,14 @@ Page({ audio.destroy(); audio = null; } + this.setData({ + isAudioPlaying: false, + currentAudioText: '' + }); + }, + + // 音频停止播放的回调 + onAudioStop() { + this.resetAudio(); } }); \ No newline at end of file diff --git a/pages/result/result.json b/pages/result/result.json index 9e26dfe..e273712 100644 --- a/pages/result/result.json +++ b/pages/result/result.json @@ -1 +1,5 @@ -{} \ No newline at end of file +{ + "usingComponents": { + "audio-overlay": "../../components/audio-overlay/audio-overlay" + } +} \ No newline at end of file diff --git a/pages/result/result.wxml b/pages/result/result.wxml index c8dd4d2..bb0803f 100644 --- a/pages/result/result.wxml +++ b/pages/result/result.wxml @@ -16,4 +16,11 @@ - \ No newline at end of file + + + + \ No newline at end of file -- libgit2 0.22.2