Files
dsProject/dsLightRag/static/XiaoZhi.html
2025-08-22 08:01:36 +08:00

266 lines
9.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>学伴切换示例</title>
<style>
.model-selector {
position: fixed;
top: 20px;
left: 20px;
z-index: 1000;
padding: 10px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
select {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
font-size: 14px;
}
.recording-indicator {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 1000;
padding: 10px 15px;
background-color: rgba(220, 53, 69, 0.9);
color: white;
border-radius: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
display: none;
align-items: center;
}
.recording-dot {
width: 10px;
height: 10px;
background-color: white;
border-radius: 50%;
margin-right: 8px;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.record-controls {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
display: none;
}
.record-button {
width: 60px;
height: 60px;
border-radius: 50%;
background-color: #dc3545;
border: none;
color: white;
font-size: 16px;
cursor: pointer;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.record-button:hover {
background-color: #c82333;
}
</style>
</head>
<body>
<div class="model-selector">
<label for="model-select">选择学伴:</label>
<select id="model-select">
<option value="shizuku">小智</option>
<option value="koharu">小荷</option>
<option value="wanko">汪喵</option>
</select>
</div>
<div class="recording-indicator" id="recordingIndicator">
<div class="recording-dot"></div>
<span>正在录音...</span>
</div>
<div class="record-controls" id="recordControls">
<button class="record-button" id="stopRecordBtn">停止</button>
</div>
<script src="https://l2dwidget.js.org/lib/L2Dwidget.min.js"></script>
<script>
// 模型配置
const models = {
shizuku: {
jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json",
name: "志津子"
},
koharu: {
jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json",
name: "小春日和"
},
wanko: {
jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json",
name: "汪喵"
}
};
// 录音相关变量
let mediaRecorder;
let audioChunks = [];
let isRecording = false;
// 获取URL参数
function getUrlParam(name) {
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
const r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
// 初始化录音功能
function initRecording() {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
console.log("录音功能初始化成功");
} else {
console.error("您的浏览器不支持录音功能");
alert("您的浏览器不支持录音功能,请使用现代浏览器");
}
}
// 开始录音
function startRecording() {
if (isRecording) return;
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) {
audioChunks.push(event.data);
}
};
mediaRecorder.onstop = () => {
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
// 这里可以处理录音完成后的音频数据例如发送到服务器进行ASR识别
console.log("录音完成,音频数据大小:", audioBlob.size);
// 示例创建音频URL并播放
const audioUrl = URL.createObjectURL(audioBlob);
console.log("录音URL:", audioUrl);
// 可以在这里调用ASR服务
};
mediaRecorder.start();
isRecording = true;
document.getElementById('recordingIndicator').style.display = 'flex';
document.getElementById('recordControls').style.display = 'block';
console.log("开始录音");
// 设置最长录音时间为60秒
setTimeout(stopRecording, 60000);
})
.catch(error => {
console.error("获取麦克风权限失败:", error);
alert("请授权麦克风权限以使用录音功能");
});
}
// 停止录音
function stopRecording() {
if (!isRecording || !mediaRecorder) return;
mediaRecorder.stop();
isRecording = false;
document.getElementById('recordingIndicator').style.display = 'none';
document.getElementById('recordControls').style.display = 'none';
console.log("停止录音");
// 停止所有音轨
if (mediaRecorder.stream) {
mediaRecorder.stream.getTracks().forEach(track => track.stop());
}
}
// 初始化看板娘
function initL2Dwidget() {
// 获取模型ID (从URL参数或默认)
const modelId = getUrlParam('id') || 'shizuku';
const model = models[modelId] || models.shizuku;
// 设置下拉框选中项
document.getElementById('model-select').value = modelId;
console.log('加载模型:', model.name, model.jsonPath);
// 初始化模型
L2Dwidget.init({
"model": {
"jsonPath": model.jsonPath,
"scale": 1
},
"display": {
"position": "right",
"width": 150,
"height": 300,
"hOffset": 0,
"vOffset": -20
},
"mobile": {
"show": true,
"scale": 0.5
},
"react": {
"opacityDefault": 0.8,
"opacityOnHover": 1
},
"dialog": {
"enable": true,
"script": {
'tap body': `你好啊,我是${model.name}。点击我可以开始录音哦~`,
'tap face': '有什么问题或者烦心事都可以和我聊聊~',
}
}
});
// 添加点击看板娘任意位置触发录音
setTimeout(() => {
const live2dElement = document.querySelector('.live2d-widget');
if (live2dElement) {
live2dElement.addEventListener('click', function(e) {
// 检查是否点击了对话框或其他控件
if (!e.target.closest('.l2d-dialog') && !e.target.closest('.model-selector') && !e.target.closest('.record-controls')) {
if (isRecording) {
stopRecording();
} else {
startRecording();
}
}
});
}
}, 1000);
}
// 监听下拉框变化
document.getElementById('model-select').addEventListener('change', function() {
const modelId = this.value;
console.log('切换模型至:', modelId);
// 更新URL参数并刷新页面
window.location.search = '?id=' + modelId;
});
// 停止录音按钮事件
document.getElementById('stopRecordBtn').addEventListener('click', stopRecording);
// 页面加载完成后初始化
window.onload = function() {
initL2Dwidget();
initRecording();
};
</script>
</body>
</html>