feat: 完善见素起名小程序功能
- 添加收藏锦囊功能,支持查看和删除收藏 - 实现积分系统,每日赠送5次灵感次数 - 添加静心阅读功能,阅读15秒可获得额外次数 - 实现灵感广场,展示用户分享的名字 - 添加字源溯源组件,长按汉字查看详情 - 优化空状态和结语卡片样式统一 - 添加音频控制(静音/风铃/雨落/古琴/白噪音/森林/溪流) - 优化名字生成逻辑,确保每次返回5个不重复名字 - 修复卡片翻转样式问题 - 移除首页动态提醒气泡
This commit is contained in:
260
miniprogram/utils/audio.js
Normal file
260
miniprogram/utils/audio.js
Normal file
@@ -0,0 +1,260 @@
|
||||
// 音频管理工具 - 环境白噪音与音效管理
|
||||
|
||||
// 背景音频管理器(用于环境白噪音)
|
||||
let bgmManager = null;
|
||||
|
||||
// 音效上下文缓存
|
||||
const soundEffects = {};
|
||||
|
||||
// 当前环境音类型
|
||||
let currentAmbienceType = 'silent';
|
||||
|
||||
// 音量渐变定时器
|
||||
let fadeTimer = null;
|
||||
|
||||
/**
|
||||
* 初始化背景音频管理器
|
||||
*/
|
||||
export const initBGM = () => {
|
||||
if (!bgmManager) {
|
||||
bgmManager = wx.getBackgroundAudioManager();
|
||||
bgmManager.title = '见素-环境音';
|
||||
bgmManager.epname = '环境白噪音';
|
||||
bgmManager.singer = '见素';
|
||||
}
|
||||
return bgmManager;
|
||||
};
|
||||
|
||||
/**
|
||||
* 播放环境白噪音
|
||||
* @param {string} type - 环境音类型: 'silent' | 'wind' | 'rain' | 'guqin' | 'white' | 'forest' | 'stream'
|
||||
* @param {boolean} fade - 是否淡入
|
||||
*/
|
||||
export const playAmbience = (type = 'wind', fade = true) => {
|
||||
const bgm = initBGM();
|
||||
|
||||
// 如果相同类型且正在播放,不重复操作
|
||||
if (currentAmbienceType === type && bgm.currentTime > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentAmbienceType = type;
|
||||
|
||||
// 静音模式
|
||||
if (type === 'silent') {
|
||||
fadeOutAndStop();
|
||||
return;
|
||||
}
|
||||
|
||||
// 音频资源映射(使用本地音频)
|
||||
const audioMap = {
|
||||
wind: '/assets/audios/wind.mp3', // 风铃
|
||||
rain: '/assets/audios/rain.mp3', // 雨声
|
||||
guqin: '/assets/audios/guqin.mp3', // 古琴
|
||||
white: '/assets/audios/white.mp3', // 白噪音
|
||||
forest: '/assets/audios/forest.mp3', // 森林
|
||||
stream: '/assets/audios/stream.mp3' // 溪流
|
||||
};
|
||||
|
||||
// 音频类型名称映射
|
||||
const typeNameMap = {
|
||||
wind: '风铃',
|
||||
rain: '雨落',
|
||||
guqin: '古琴',
|
||||
white: '白噪音',
|
||||
forest: '森林',
|
||||
stream: '溪流'
|
||||
};
|
||||
|
||||
const src = audioMap[type];
|
||||
if (!src) return;
|
||||
|
||||
// 设置音频属性
|
||||
bgm.title = `见素 - ${typeNameMap[type] || '环境音'}`;
|
||||
bgm.singer = '环境音';
|
||||
bgm.coverImgUrl = '';
|
||||
|
||||
// 监听错误事件
|
||||
bgm.onError((err) => {
|
||||
console.error('音频播放错误:', err);
|
||||
wx.showToast({
|
||||
title: '音频加载失败',
|
||||
icon: 'none'
|
||||
});
|
||||
});
|
||||
|
||||
// 淡入效果
|
||||
if (fade) {
|
||||
bgm.volume = 0;
|
||||
bgm.src = src;
|
||||
bgm.play();
|
||||
fadeIn(0.3, 1000); // 1秒内淡入到30%音量
|
||||
} else {
|
||||
bgm.volume = 0.3;
|
||||
bgm.src = src;
|
||||
bgm.play();
|
||||
}
|
||||
|
||||
console.log(`播放环境音: ${type}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* 淡入音量
|
||||
* @param {number} targetVolume - 目标音量 0-1
|
||||
* @param {number} duration - 淡入时长 ms
|
||||
*/
|
||||
const fadeIn = (targetVolume = 0.3, duration = 1000) => {
|
||||
if (!bgmManager) return;
|
||||
|
||||
clearInterval(fadeTimer);
|
||||
const step = targetVolume / (duration / 50); // 每50ms调整一次
|
||||
|
||||
fadeTimer = setInterval(() => {
|
||||
if (bgmManager.volume < targetVolume) {
|
||||
bgmManager.volume = Math.min(bgmManager.volume + step, targetVolume);
|
||||
} else {
|
||||
clearInterval(fadeTimer);
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* 淡出并停止
|
||||
* @param {number} duration - 淡出时长 ms
|
||||
*/
|
||||
export const fadeOutAndStop = (duration = 500) => {
|
||||
if (!bgmManager) return;
|
||||
|
||||
clearInterval(fadeTimer);
|
||||
const startVolume = bgmManager.volume;
|
||||
const step = startVolume / (duration / 50);
|
||||
|
||||
fadeTimer = setInterval(() => {
|
||||
if (bgmManager.volume > 0.01) {
|
||||
bgmManager.volume = Math.max(bgmManager.volume - step, 0);
|
||||
} else {
|
||||
clearInterval(fadeTimer);
|
||||
bgmManager.stop();
|
||||
currentAmbienceType = 'silent';
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* 播放音效
|
||||
* @param {string} type - 音效类型
|
||||
* @param {object} options - 配置选项
|
||||
*/
|
||||
export const playSoundEffect = (type, options = {}) => {
|
||||
const { volume = 1, loop = false } = options;
|
||||
|
||||
// 音效资源映射
|
||||
const effectMap = {
|
||||
flip: '/assets/audios/paper.mp3', // 翻页/纸张摩擦声
|
||||
success: '/assets/audios/success.mp3', // 成功
|
||||
inkDrop: '/assets/audios/inkdrop.mp3', // 水滴/入墨声
|
||||
swipe: '/assets/audios/swipe.mp3', // 滑动
|
||||
tap: '/assets/audios/tap.mp3', // 点击
|
||||
breathe: '/assets/audios/breathe.mp3', // 呼吸
|
||||
char: '/assets/audios/char.mp3' // 字显现
|
||||
};
|
||||
|
||||
const src = effectMap[type];
|
||||
if (!src) return;
|
||||
|
||||
// 复用或创建音频上下文
|
||||
if (!soundEffects[type]) {
|
||||
soundEffects[type] = wx.createInnerAudioContext();
|
||||
}
|
||||
|
||||
const ctx = soundEffects[type];
|
||||
ctx.src = src;
|
||||
ctx.volume = volume;
|
||||
ctx.loop = loop;
|
||||
|
||||
ctx.stop();
|
||||
try {
|
||||
ctx.play();
|
||||
} catch (err) {
|
||||
console.log('音效播放失败:', err);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止指定音效
|
||||
* @param {string} type - 音效类型
|
||||
*/
|
||||
export const stopSoundEffect = (type) => {
|
||||
if (soundEffects[type]) {
|
||||
soundEffects[type].stop();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止所有音效
|
||||
*/
|
||||
export const stopAllSoundEffects = () => {
|
||||
Object.keys(soundEffects).forEach(type => {
|
||||
soundEffects[type].stop();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 停止环境音
|
||||
*/
|
||||
export const stopAmbience = () => {
|
||||
fadeOutAndStop();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前环境音类型
|
||||
*/
|
||||
export const getCurrentAmbience = () => currentAmbienceType;
|
||||
|
||||
/**
|
||||
* 切换环境音
|
||||
* @param {string} type - 环境音类型
|
||||
*/
|
||||
export const toggleAmbience = (type) => {
|
||||
if (currentAmbienceType === type) {
|
||||
playAmbience('silent');
|
||||
} else {
|
||||
playAmbience(type);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 呼吸震动效果 - 配合名字逐字显现
|
||||
* @param {number} duration - 总时长 ms
|
||||
* @param {number} intensity - 震动强度 light/medium/heavy
|
||||
*/
|
||||
export const breatheVibration = (duration = 2000, intensity = 'light') => {
|
||||
const interval = duration / 4; // 分4次震动
|
||||
let count = 0;
|
||||
|
||||
const vibrate = () => {
|
||||
if (count < 4) {
|
||||
wx.vibrateShort({ type: intensity });
|
||||
count++;
|
||||
setTimeout(vibrate, interval);
|
||||
}
|
||||
};
|
||||
|
||||
vibrate();
|
||||
};
|
||||
|
||||
/**
|
||||
* 清理音频资源
|
||||
*/
|
||||
export const cleanup = () => {
|
||||
clearInterval(fadeTimer);
|
||||
if (bgmManager) {
|
||||
bgmManager.stop();
|
||||
}
|
||||
Object.keys(soundEffects).forEach(type => {
|
||||
soundEffects[type].destroy();
|
||||
delete soundEffects[type];
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user