'commit'
This commit is contained in:
@@ -277,6 +277,101 @@
|
||||
padding: 10px 15px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
/* 学伴模型选择器样式 */
|
||||
.model-selector {
|
||||
position: fixed; top: 40px; 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);
|
||||
display: flex; flex-direction: column; gap: 15px;
|
||||
}
|
||||
select { padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; background-color: white; font-size: 14px; }
|
||||
|
||||
/* 录音控制样式 */
|
||||
.recording-controls {
|
||||
position: static; display: flex; flex-direction: column; gap: 10px; margin: 0;
|
||||
}
|
||||
.record-button {
|
||||
width: 70px; height: 70px; 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);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 1 !important; visibility: visible !important;
|
||||
}
|
||||
.record-button:hover { background-color: #c82333; }
|
||||
.stop-button {
|
||||
width: 70px; height: 70px; border-radius: 50%;
|
||||
background-color: #6c757d; border: none; color: white;
|
||||
font-size: 16px; cursor: pointer; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
display: flex; align-items: center; justify-content: center; display: none;
|
||||
opacity: 1 !important; visibility: visible !important;
|
||||
}
|
||||
|
||||
/* 录音指示器样式 */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* 思考中动画样式 */
|
||||
.thinking-indicator {
|
||||
position: fixed; bottom: 20px; right: 120px; z-index: 1000;
|
||||
padding: 10px 15px; background-color: rgba(0, 123, 255, 0.9);
|
||||
color: white; border-radius: 20px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
display: none; align-items: center;
|
||||
}
|
||||
.thinking-dots { display: flex; gap: 4px; margin-right: 8px; }
|
||||
.thinking-dot {
|
||||
width: 8px; height: 8px; background-color: white; border-radius: 50%;
|
||||
}
|
||||
.thinking-dot:nth-child(1) { animation: pulse 1.5s infinite 0s; }
|
||||
.thinking-dot:nth-child(2) { animation: pulse 1.5s infinite 0.3s; }
|
||||
.thinking-dot:nth-child(3) { animation: pulse 1.5s infinite 0.6s; }
|
||||
|
||||
/* 结果显示容器样式 */
|
||||
.result-container {
|
||||
position: fixed; bottom: 80px; left: 20px; z-index: 1000;
|
||||
padding: 15px; background-color: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 8px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
max-width: 400px; width: 90%;
|
||||
display: none; flex-direction: column; gap: 10px;
|
||||
}
|
||||
.result-header {
|
||||
font-weight: bold; color: #495057; margin-bottom: 5px;
|
||||
display: flex; align-items: center; gap: 5px;
|
||||
}
|
||||
.result-header.asr { color: #007bff; }
|
||||
.result-header.feedback { color: #28a745; }
|
||||
.result-text {
|
||||
color: #333; line-height: 1.5; max-height: 200px; overflow-y: auto;
|
||||
padding-right: 5px; word-break: break-all;
|
||||
}
|
||||
|
||||
/* 音频播放器样式 */
|
||||
.audio-player-container {
|
||||
margin-top: 10px; display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.play-button {
|
||||
background-color: #28a745; color: white; border: none;
|
||||
border-radius: 50%; width: 40px; height: 40px;
|
||||
cursor: pointer; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.play-button:hover { background-color: #218838; }
|
||||
.audio-progress {
|
||||
flex-grow: 1; height: 6px; background-color: #e9ecef; border-radius: 3px;
|
||||
overflow: hidden; cursor: pointer;
|
||||
}
|
||||
.progress-bar {
|
||||
height: 100%; background-color: #28a745; width: 0%;
|
||||
}
|
||||
.audio-time { font-size: 12px; color: #6c757d; }
|
||||
|
||||
@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -987,4 +1082,189 @@
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
<!-- 学伴功能区域 -->
|
||||
<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="thinking-indicator" id="thinkingIndicator">
|
||||
<div class="thinking-dots">
|
||||
<div class="thinking-dot"></div>
|
||||
<div class="thinking-dot"></div>
|
||||
<div class="thinking-dot"></div>
|
||||
</div>
|
||||
<span>学伴正在思考中...</span>
|
||||
</div>
|
||||
|
||||
<div class="recording-controls" style="position: fixed; right:37px; bottom: 230px; z-index: 998;">
|
||||
<button class="record-button" id="startRecordBtn" style="font-size: 14px;">和我讲话</button>
|
||||
<button class="stop-button" id="stopRecordBtn" style="font-size: 14px;">停止讲话</button>
|
||||
</div>
|
||||
|
||||
<div class="result-container" id="resultContainer">
|
||||
<div>
|
||||
<div class="result-header asr">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 1H4C2.89 1 2 1.9 2 3V13C2 14.1 2.89 15 4 15H12C13.11 15 14 14.1 14 13V3C14 1.9 13.11 1 12 1ZM12 13H4V3H12V13Z" fill="#007bff"/>
|
||||
<path d="M8 4C6.34 4 5 5.34 5 7C5 8.66 6.34 10 8 10C9.66 10 11 8.66 11 7C11 5.34 9.66 4 8 4ZM8 8.5C7.17 8.5 6.5 7.83 6.5 7C6.5 6.17 7.17 5.5 8 5.5C8.83 5.5 9.5 6.17 9.5 7C9.5 7.83 8.83 8.5 8 8.5Z" fill="#007bff"/>
|
||||
<path d="M8 11C5.79 11 4 12.79 4 15V16H12V15C12 12.79 10.21 11 8 11ZM8 13C9.1 13 10 13.9 10 15H6C6 13.9 6.9 13 8 13Z" fill="#007bff"/>
|
||||
</svg>
|
||||
你讲的话
|
||||
</div>
|
||||
<div class="result-text" id="asrResultText"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="result-header feedback">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 4C1 2.9 1.9 2 3 2H13C14.1 2 15 2.9 15 4V11C15 12.1 14.1 13 13 13H9L5 16V13H3C1.9 13 1 12.1 1 11V4Z" fill="#28a745"/>
|
||||
<path d="M8 8H6V5H8V8ZM11 8H9V5H11V8ZM11 11H6V9H11V11Z" fill="#28a745"/>
|
||||
</svg>
|
||||
学伴回复
|
||||
</div>
|
||||
<div class="result-text" id="feedbackResultText"></div>
|
||||
</div>
|
||||
<div class="audio-player-container">
|
||||
<button class="play-button" id="playAudioBtn">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 5V19L19 12L8 5Z" fill="white"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="audio-progress" id="audioProgress">
|
||||
<div class="progress-bar" id="progressBar"></div>
|
||||
</div>
|
||||
<div class="audio-time" id="audioTime">00:00 / 00:00</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
有权限问题的话,请点击<a href="https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/11%E3%80%81%E5%AD%A6%E4%BC%B4Chrome%E5%BD%95%E9%9F%B3%E9%85%8D%E7%BD%AE%E4%BF%AE%E6%94%B9.pdf">这里</a>
|
||||
</div>
|
||||
<script src="https://l2dwidget.js.org/lib/L2Dwidget.min.js"></script>
|
||||
<script>
|
||||
// 模型配置映射
|
||||
const modelConfig = {
|
||||
shizuku: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-shizuku@1.0.5/assets/shizuku.model.json"
|
||||
},
|
||||
koharu: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-koharu@1.0.5/assets/koharu.model.json"
|
||||
},
|
||||
wanko: {
|
||||
jsonPath: "https://unpkg.com/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json"
|
||||
}
|
||||
};
|
||||
|
||||
let currentL2DWidget = null;
|
||||
let modelElement = null;
|
||||
|
||||
// 获取URL参数
|
||||
function getUrlParam(name) {
|
||||
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
|
||||
const r = window.location.search.substr(1).match(reg);
|
||||
return r ? unescape(r[2]) : null;
|
||||
}
|
||||
|
||||
// 初始化看板娘
|
||||
function initL2DWidget(modelName) {
|
||||
// 如果没有传入modelName,则从URL参数获取模型名称,默认使用shizuku
|
||||
if (!modelName) {
|
||||
modelName = getUrlParam('model') || 'shizuku';
|
||||
}
|
||||
console.log("切换模型: ", modelName);
|
||||
const config = modelConfig[modelName];
|
||||
if (!config) {
|
||||
console.error("模型配置不存在: ", modelName);
|
||||
return;
|
||||
}
|
||||
console.log("模型JSON路径: ", config.jsonPath);
|
||||
|
||||
// 彻底清理现有实例
|
||||
if (currentL2DWidget) {
|
||||
try {
|
||||
currentL2DWidget.destroy();
|
||||
console.log("旧模型实例已销毁");
|
||||
} catch (e) {
|
||||
console.error("销毁实例失败: ", e);
|
||||
}
|
||||
// 修复:强制移除所有canvas元素
|
||||
const oldCanvases = document.querySelectorAll('canvas#l2dcanvas');
|
||||
oldCanvases.forEach(canvas => canvas.remove());
|
||||
currentL2DWidget = null;
|
||||
modelElement = null;
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
try {
|
||||
currentL2DWidget = L2Dwidget.init({
|
||||
model: {
|
||||
jsonPath: config.jsonPath,
|
||||
scale: 1
|
||||
},
|
||||
display: {
|
||||
position: "right",
|
||||
width: 150,
|
||||
height: 300,
|
||||
hOffset: 0,
|
||||
vOffset: -20
|
||||
},
|
||||
mobile: {
|
||||
show: true,
|
||||
scale: 0.5
|
||||
},
|
||||
react: {
|
||||
opacityDefault: 0.7,
|
||||
opacityOnHover: 0.8
|
||||
}
|
||||
});
|
||||
// 保存模型DOM元素引用
|
||||
// 修复:使用setTimeout确保DOM更新完成后再获取元素
|
||||
setTimeout(() => {
|
||||
modelElement = document.querySelector('canvas#l2dcanvas');
|
||||
if (modelElement) {
|
||||
console.log("新模型DOM元素已获取");
|
||||
} else {
|
||||
console.warn("未找到新模型canvas元素");
|
||||
}
|
||||
console.log("新模型初始化成功");
|
||||
}, 100);
|
||||
} catch (e) {
|
||||
console.error("模型初始化失败: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听模型选择变化
|
||||
const modelSelect = document.getElementById('model-select');
|
||||
if (modelSelect) {
|
||||
// 设置当前选中项
|
||||
const currentModel = getUrlParam('model') || 'shizuku';
|
||||
modelSelect.value = currentModel;
|
||||
|
||||
modelSelect.addEventListener('change', function() {
|
||||
console.log("选择模型: ", this.value);
|
||||
// 通过URL参数刷新页面实现模型切换
|
||||
window.location.search = '?model=' + this.value;
|
||||
});
|
||||
console.log("模型选择监听器已绑定");
|
||||
} else {
|
||||
console.error("未找到模型选择器元素");
|
||||
}
|
||||
|
||||
// 初始加载默认模型
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
//const defaultModel = modelSelect ? modelSelect.value : 'shizuku';
|
||||
//console.log("初始加载模型: ", defaultModel);
|
||||
initL2DWidget();
|
||||
});
|
||||
</script>
|
Reference in New Issue
Block a user