9563730a6f
Implement floating button that toggles a menu with video options Add styling for floating elements and handle click interactions Update video switching logic to prevent unintended transitions
240 lines
9.7 KiB
JavaScript
240 lines
9.7 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
// --- 加载屏幕处理 ---
|
|
const loadingScreen = document.getElementById('loading-screen');
|
|
setTimeout(() => {
|
|
loadingScreen.style.opacity = '0';
|
|
// 在动画结束后将其隐藏,以防它阻碍交互
|
|
setTimeout(() => {
|
|
loadingScreen.style.display = 'none';
|
|
}, 500); // 这个时间应该匹配 CSS 中的 transition 时间
|
|
}, 1500); // 1.5秒后开始淡出
|
|
|
|
// 获取需要的 DOM 元素
|
|
let video1 = document.getElementById('video1');
|
|
let video2 = document.getElementById('video2');
|
|
const micButton = document.getElementById('mic-button');
|
|
const favorabilityBar = document.getElementById('favorability-bar');
|
|
const floatingButton = document.getElementById('floating-button');
|
|
const menuContainer = document.getElementById('menu-container');
|
|
const menuItems = document.querySelectorAll('.menu-item');
|
|
|
|
let activeVideo = video1;
|
|
let inactiveVideo = video2;
|
|
|
|
// 视频列表
|
|
const videoList = [
|
|
'视频资源/3D 建模图片制作.mp4',
|
|
'视频资源/jimeng-2025-07-16-1043-笑着优雅的左右摇晃,过一会儿手扶着下巴,保持微笑.mp4',
|
|
'视频资源/jimeng-2025-07-16-4437-比耶,然后微笑着优雅的左右摇晃.mp4',
|
|
'视频资源/生成加油视频.mp4',
|
|
'视频资源/生成跳舞视频.mp4',
|
|
'视频资源/负面/jimeng-2025-07-16-9418-双手叉腰,嘴巴一直在嘟囔,表情微微生气.mp4'
|
|
];
|
|
|
|
// --- 视频交叉淡入淡出播放功能 ---
|
|
function switchVideo() {
|
|
// 1. 选择下一个视频
|
|
const currentVideoSrc = activeVideo.querySelector('source').getAttribute('src');
|
|
let nextVideoSrc = currentVideoSrc;
|
|
while (nextVideoSrc === currentVideoSrc) {
|
|
const randomIndex = Math.floor(Math.random() * videoList.length);
|
|
nextVideoSrc = videoList[randomIndex];
|
|
}
|
|
|
|
// 2. 设置不活动的 video 元素的 source
|
|
inactiveVideo.querySelector('source').setAttribute('src', nextVideoSrc);
|
|
inactiveVideo.load();
|
|
|
|
// 3. 当不活动的视频可以播放时,执行切换
|
|
inactiveVideo.addEventListener('canplaythrough', function onCanPlayThrough() {
|
|
// 确保事件只触发一次
|
|
inactiveVideo.removeEventListener('canplaythrough', onCanPlayThrough);
|
|
|
|
// 4. 播放新视频
|
|
inactiveVideo.play().catch(error => {
|
|
console.error("Video play failed:", error);
|
|
});
|
|
|
|
// 5. 切换 active class 来触发 CSS 过渡
|
|
activeVideo.classList.remove('active');
|
|
inactiveVideo.classList.add('active');
|
|
|
|
// 6. 更新角色
|
|
[activeVideo, inactiveVideo] = [inactiveVideo, activeVideo];
|
|
|
|
// 为新的 activeVideo 绑定 ended 事件
|
|
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
|
}, { once: true }); // 使用 { once: true } 确保事件只被处理一次
|
|
}
|
|
|
|
// 初始启动
|
|
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
|
|
|
|
|
// --- 语音识别核心 ---
|
|
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
let recognition;
|
|
|
|
// 检查浏览器是否支持语音识别
|
|
if (SpeechRecognition) {
|
|
recognition = new SpeechRecognition();
|
|
recognition.continuous = true; // 持续识别
|
|
recognition.lang = 'zh-CN'; // 设置语言为中文
|
|
recognition.interimResults = true; // 获取临时结果
|
|
|
|
recognition.onresult = (event) => {
|
|
const transcriptContainer = document.getElementById('transcript');
|
|
let final_transcript = '';
|
|
let interim_transcript = '';
|
|
|
|
for (let i = event.resultIndex; i < event.results.length; ++i) {
|
|
if (event.results[i].isFinal) {
|
|
final_transcript += event.results[i][0].transcript;
|
|
} else {
|
|
interim_transcript += event.results[i][0].transcript;
|
|
}
|
|
}
|
|
|
|
// 显示最终识别结果
|
|
transcriptContainer.textContent = final_transcript || interim_transcript;
|
|
|
|
// 基于关键词的情感分析和视频切换
|
|
if (final_transcript) {
|
|
analyzeAndReact(final_transcript);
|
|
}
|
|
};
|
|
|
|
recognition.onerror = (event) => {
|
|
console.error('语音识别错误:', event.error);
|
|
};
|
|
|
|
} else {
|
|
console.log('您的浏览器不支持语音识别功能。');
|
|
// 可以在界面上给用户提示
|
|
}
|
|
|
|
// --- 麦克风按钮交互 ---
|
|
let isListening = false;
|
|
|
|
micButton.addEventListener('click', function() {
|
|
if (!SpeechRecognition) return; // 如果不支持,则不执行任何操作
|
|
|
|
isListening = !isListening;
|
|
micButton.classList.toggle('is-listening', isListening);
|
|
const transcriptContainer = document.querySelector('.transcript-container');
|
|
const transcriptText = document.getElementById('transcript');
|
|
|
|
if (isListening) {
|
|
transcriptText.textContent = '聆听中...'; // 立刻显示提示
|
|
transcriptContainer.classList.add('visible');
|
|
recognition.start();
|
|
} else {
|
|
recognition.stop();
|
|
transcriptContainer.classList.remove('visible');
|
|
transcriptText.textContent = ''; // 清空文本
|
|
}
|
|
});
|
|
|
|
|
|
// --- 悬浮按钮交互 ---
|
|
floatingButton.addEventListener('click', (event) => {
|
|
event.stopPropagation(); // 防止事件冒泡到 document
|
|
menuContainer.classList.toggle('hidden');
|
|
});
|
|
|
|
menuItems.forEach(item => {
|
|
item.addEventListener('click', function() {
|
|
const videoSrc = this.getAttribute('data-video');
|
|
playSpecificVideo(videoSrc);
|
|
menuContainer.classList.add('hidden');
|
|
});
|
|
});
|
|
|
|
// 点击菜单外部区域关闭菜单
|
|
document.addEventListener('click', () => {
|
|
if (!menuContainer.classList.contains('hidden')) {
|
|
menuContainer.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// 阻止菜单自身的点击事件冒泡
|
|
menuContainer.addEventListener('click', (event) => {
|
|
event.stopPropagation();
|
|
});
|
|
|
|
|
|
function playSpecificVideo(videoSrc) {
|
|
const currentVideoSrc = activeVideo.querySelector('source').getAttribute('src');
|
|
if (videoSrc === currentVideoSrc) return;
|
|
|
|
inactiveVideo.querySelector('source').setAttribute('src', videoSrc);
|
|
inactiveVideo.load();
|
|
|
|
inactiveVideo.addEventListener('canplaythrough', function onCanPlayThrough() {
|
|
inactiveVideo.removeEventListener('canplaythrough', onCanPlayThrough);
|
|
activeVideo.pause(); // 暂停当前视频,防止其 'ended' 事件触发切换
|
|
inactiveVideo.play().catch(error => console.error("Video play failed:", error));
|
|
activeVideo.classList.remove('active');
|
|
inactiveVideo.classList.add('active');
|
|
[activeVideo, inactiveVideo] = [inactiveVideo, activeVideo];
|
|
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
|
}, { once: true });
|
|
}
|
|
|
|
// --- 情感分析与反应 ---
|
|
const positiveWords = ['开心', '高兴', '喜欢', '太棒了', '你好', '漂亮'];
|
|
const negativeWords = ['难过', '生气', '讨厌', '伤心'];
|
|
|
|
const positiveVideos = [
|
|
'视频资源/jimeng-2025-07-16-1043-笑着优雅的左右摇晃,过一会儿手扶着下巴,保持微笑.mp4',
|
|
'视频资源/jimeng-2025-07-16-4437-比耶,然后微笑着优雅的左右摇晃.mp4',
|
|
'视频资源/生成加油视频.mp4',
|
|
'视频资源/生成跳舞视频.mp4'
|
|
];
|
|
const negativeVideo = '视频资源/负面/jimeng-2025-07-16-9418-双手叉腰,嘴巴一直在嘟囔,表情微微生气.mp4';
|
|
|
|
function analyzeAndReact(text) {
|
|
let reaction = 'neutral'; // 默认为中性
|
|
|
|
if (positiveWords.some(word => text.includes(word))) {
|
|
reaction = 'positive';
|
|
} else if (negativeWords.some(word => text.includes(word))) {
|
|
reaction = 'negative';
|
|
}
|
|
|
|
if (reaction !== 'neutral') {
|
|
switchVideoByEmotion(reaction);
|
|
}
|
|
}
|
|
|
|
function switchVideoByEmotion(emotion) {
|
|
let nextVideoSrc;
|
|
if (emotion === 'positive') {
|
|
const randomIndex = Math.floor(Math.random() * positiveVideos.length);
|
|
nextVideoSrc = positiveVideos[randomIndex];
|
|
} else { // negative
|
|
nextVideoSrc = negativeVideo;
|
|
}
|
|
|
|
// 避免重复播放同一个视频
|
|
const currentVideoSrc = activeVideo.querySelector('source').getAttribute('src');
|
|
if (nextVideoSrc === currentVideoSrc) return;
|
|
|
|
// --- 以下逻辑与 switchVideo 函数类似,用于切换视频 ---
|
|
inactiveVideo.querySelector('source').setAttribute('src', nextVideoSrc);
|
|
inactiveVideo.load();
|
|
|
|
inactiveVideo.addEventListener('canplaythrough', function onCanPlayThrough() {
|
|
inactiveVideo.removeEventListener('canplaythrough', onCanPlayThrough);
|
|
activeVideo.pause(); // 暂停当前视频,防止其 'ended' 事件触发切换
|
|
inactiveVideo.play().catch(error => console.error("Video play failed:", error));
|
|
activeVideo.classList.remove('active');
|
|
inactiveVideo.classList.add('active');
|
|
[activeVideo, inactiveVideo] = [inactiveVideo, activeVideo];
|
|
// 情感触发的视频播放结束后,回归随机播放
|
|
activeVideo.addEventListener('ended', switchVideo, { once: true });
|
|
}, { once: true });
|
|
}
|
|
|
|
}); |