使用 HTML + JavaScript 实现文章逐句高亮朗读功能
在这个信息爆炸的时代,我们每天都要面对大量的文字阅读。无论是学习、工作还是个人成长,阅读都扮演着至关重要的角色。然而,在快节奏的生活中,我们往往难以找到足够的安静时间专注于阅读。本文用 HTML + JavaScript 实现了一个基于Web的语音文章朗读器,为您带来全新的阅读体验。
效果演示
项目核心
本项目主要包含以下核心功能:
- 语音合成(Text-to-Speech)功能
- 控制播放、暂停、继续和停止操作
- 语音选择功能
- 阅读进度保存与恢复
- 句子级高亮显示
- 点击任意句子直接跳转并朗读
页面结构
控制区域
包含所有操作按钮(开始、暂停、继续、停止、重置)和语音选择下拉框。
<div class="controls"><button id="playBtn">开始朗读</button><button id="pauseBtn" disabled>暂停</button><button id="resumeBtn" disabled>继续</button><button id="stopBtn" disabled>停止</button><select id="voiceSelect" class="voice-select"></select><button id="resetBtn">重置进度</button>
</div>
文章区域
包含多个段落,每个段落由多个可交互的句子组成。
<div class="article" id="article"><p class="paragraph"><span class="sentence">在编程的世界里,学习是一个永无止境的过程。</span><span class="sentence">随着技术的不断发展,我们需要不断更新自己的知识和技能。</span><span class="sentence">HTML、CSS和JavaScript是构建现代网页的三大基石。</span></p><p class="paragraph"><span class="sentence">掌握这些基础技术后,你可以进一步学习各种前端框架和工具。</span><span class="sentence">React、Vue和Angular是目前最流行的前端框架。</span><span class="sentence">它们都采用了组件化的开发模式,提高了代码的可维护性和复用性。</span></p><p class="paragraph"><span class="sentence">除了前端技术,后端开发也是全栈工程师必须掌握的技能。</span><span class="sentence">Node.js让JavaScript可以用于服务器端编程,大大扩展了JavaScript的应用范围。</span><span class="sentence">数据库技术也是开发中的重要组成部分。</span></p>
</div>
进度信息
显示当前阅读进度。
<div class="progress-info">当前进度: <span id="progressText">0/0</span><div class="progress-bar-container"><div class="progress-bar"></div></div>
</div>
核心功能实现
定义基础变量
获取DOM元素
const sentences = document.querySelectorAll('.sentence');
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resumeBtn = document.getElementById('resumeBtn');
const stopBtn = document.getElementById('stopBtn');
const resetBtn = document.getElementById('resetBtn');
const voiceSelect = document.getElementById('voiceSelect');
const progressText = document.getElementById('progressText');
const progressBar = document.querySelector('.progress-bar');
定义语音合成相关变量
let speechSynthesis = window.speechSynthesis;
let voices = [];
let currentUtterance = null;
let currentSentenceIndex = 0;
let isPaused = false;
语音合成初始化
通过 window.speechSynthesis API 获取系统支持的语音列表,并填充到下拉选择框中。
function initSpeechSynthesis() {// 获取可用的语音列表voices = speechSynthesis.getVoices();// 填充语音选择下拉框voiceSelect.innerHTML = '';voices.forEach((voice, index) => {const option = document.createElement('option');option.value = index;option.textContent = `${voice.name} (${voice.lang})`;voiceSelect.appendChild(option);});// 尝试选择中文语音const chineseVoice = voices.find(voice =>{voice.lang.includes('zh') || voice.lang.includes('cmn')});if (chineseVoice) {const voiceIndex = voices.indexOf(chineseVoice);voiceSelect.value = voiceIndex;}
}
句子朗读功能
function speakSentence(index) {if (index >= sentences.length || index < 0) return;// 停止当前朗读if (currentUtterance) {speechSynthesis.cancel();}// 更新当前句子高亮updateHighlight(index);// 创建新的语音合成实例const selectedVoiceIndex = voiceSelect.value;const utterance = new SpeechSynthesisUtterance(sentences[index].textContent);if (voices[selectedVoiceIndex]) {utterance.voice = voices[selectedVoiceIndex];}utterance.rate = 0.9; // 稍微慢一点的语速// 朗读开始时的处理utterance.onstart = function() {sentences[index].classList.add('reading');playBtn.disabled = true;pauseBtn.disabled = false;resumeBtn.disabled = true;stopBtn.disabled = false;};// 朗读结束时的处理utterance.onend = function() {sentences[index].classList.remove('reading');if (!isPaused) {if (currentSentenceIndex >= sentences.length - 1) {// 朗读完成playBtn.disabled = false;pauseBtn.disabled = true;resumeBtn.disabled = true;stopBtn.disabled = true;updateProgressText();return;}currentSentenceIndex++;saveProgress();speakSentence(currentSentenceIndex);}};// 开始朗读currentUtterance = utterance;speechSynthesis.speak(utterance);updateProgressText();
}
句子高亮功能
function updateHighlight(index) {sentences.forEach((sentence, i) => {sentence.classList.remove('current');if (i === index) {sentence.classList.add('current');// 滚动到当前句子sentence.scrollIntoView({ behavior: 'smooth', block: 'center' });}});
}
更新进度文本
function updateProgressText() {progressText.textContent = `${currentSentenceIndex + 1}/${sentences.length}`;const percentage = (currentSentenceIndex + 1) / sentences.length * 100;progressBar.style.width = `${percentage}%`;
}
进度保存与恢复
保存进度到本地存储
function saveProgress() {localStorage.setItem('readingProgress', currentSentenceIndex);localStorage.setItem('articleId', 'demoArticle'); updateProgressText();
}
从本地存储加载进度
function loadProgress() {const savedArticleId = localStorage.getItem('articleId');if (savedArticleId === 'demoArticle') {const savedProgress = localStorage.getItem('readingProgress');if (savedProgress !== null) {currentSentenceIndex = parseInt(savedProgress);if (currentSentenceIndex >= sentences.length) {currentSentenceIndex = 0;}updateHighlight(currentSentenceIndex);updateProgressText();}}
}
点击句子朗读跳转功能
sentences.forEach((sentence, index) => {sentence.addEventListener('click', function() {currentSentenceIndex = index;speakSentence(currentSentenceIndex);});
});
扩展建议
- 语速调节:增加语速调节滑块,让用户自定义朗读速
- 多语言支持:自动检测文本语言并选择合适的语音引擎
- 断句优化:改进自然语言处理逻辑,使朗读更符合口语习惯
- 多文章支持:扩展文章管理系统,允许用户选择不同文章进行朗读
完整代码
<!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>body {font-family: 'Microsoft YaHei', sans-serif;line-height: 1.6;max-width: 800px;margin: 0 auto;padding: 40px 20px;color: #333;height: 100vh;box-sizing: border-box;background: linear-gradient(to bottom right, #f8f9fa, #e9ecef);}h1 {text-align: center;color: #2c3e50;margin-bottom: 40px;font-size: 2.5em;letter-spacing: 2px;position: relative;animation: fadeInDown 1s ease-out forwards;}@keyframes fadeInDown {from {opacity: 0;transform: translateY(-30px);}to {opacity: 1;transform: translateY(0);}}h1::after {content: '';display: block;width: 100px;height: 4px;background: linear-gradient(to right, #3498db, #2980b9);margin: 15px auto 0;border-radius: 2px;animation: growLine 1s ease-out forwards;}@keyframes growLine {from {width: 0;}to {width: 100px;}}.controls {box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);border-radius: 10px;padding: 20px;background-color: #ffffffcc;display: flex;flex-direction: column;gap: 15px;margin-bottom: 30px;}.controls > div {display: flex;flex-wrap: wrap;justify-content: center;gap: 15px;}button {padding: 10px 20px;background: linear-gradient(135deg, #3498db, #2980b9);color: white;border: none;border-radius: 25px;cursor: pointer;font-size: 16px;transition: all 0.3s ease-in-out;box-shadow: 0 4px 6px rgba(52, 152, 219, 0.3);}button:hover {transform: translateY(-2px);box-shadow: 0 6px 12px rgba(52, 152, 219, 0.4);}button:disabled {background: linear-gradient(135deg, #95a5a6, #7f8c8d);box-shadow: none;transform: none;}.article {font-size: 18px;line-height: 1.8;background-color: #ffffffee;border-radius: 10px;padding: 25px;margin-top: 30px;box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);margin-bottom: 30px;position: relative;z-index: 0}.article::before {content: '';position: absolute;top: 0;left: 0;width: 100%;height: 100%;background: radial-gradient(circle at top left, rgba(52, 152, 219, 0.05) 0%, transparent 100%);z-index: -1;border-radius: 10px;}.paragraph {margin-bottom: 20px;}.sentence {border-radius: 3px;transition: all 0.3s ease-in-out;cursor: pointer;position: relative;z-index: 1;}.sentence:hover {background-color: #f0f0f0;}.sentence::after {content: '';position: absolute;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(255, 255, 255, 0.3);opacity: 0;z-index: -1;transition: opacity 0.3s ease-in-out;}.sentence:hover::after {opacity: 1;}.current {background-color: #fffde7 !important;font-weight: bold;transform: scale(1.05);box-shadow: 0 2px 8px rgba(255, 221, 0, 0.3);}.progress-info {text-align: center;margin-top: 20px;font-size: 14px;color: #7f8c8d;}select {padding: 8px;border-radius: 4px;border: 1px solid #bdc3c7;font-size: 16px;}.voice-select {min-width: 220px;padding: 10px 12px;border-radius: 25px;border: 1px solid #bdc3c7;font-size: 16px;background-color: #f8f9fa;transition: all 0.3s ease-in-out;appearance: none;background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24'%3E%3Cpath fill='%23555' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");background-repeat: no-repeat;background-position: right 15px center;background-size: 12px;display: block;margin: 0 auto;}.voice-select:focus {outline: none;border-color: #3498db;box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);}.progress-info {text-align: center;margin-top: 30px;font-size: 14px;color: #7f8c8d;position: relative;height: 30px;}.progress-bar-container {width: 100%;height: 6px;background-color: #ecf0f1;border-radius: 3px;overflow: hidden;margin: 10px 0;}.progress-bar {height: 100%;width: 0;background: linear-gradient(to right, #3498db, #2980b9);transition: width 0.3s ease-in-out;}</style>
</head>
<body>
<h1>文章逐句高亮朗读</h1><div class="controls"><div><button id="playBtn">开始朗读</button><button id="pauseBtn" disabled>暂停</button><button id="resumeBtn" disabled>继续</button><button id="stopBtn" disabled>停止</button><button id="resetBtn">重置进度</button></div><select id="voiceSelect" class="voice-select"></select>
</div><div class="article" id="article"><p class="paragraph"><span class="sentence">在编程的世界里,学习是一个永无止境的过程。</span><span class="sentence">随着技术的不断发展,我们需要不断更新自己的知识和技能。</span><span class="sentence">HTML、CSS和JavaScript是构建现代网页的三大基石。</span></p><p class="paragraph"><span class="sentence">掌握这些基础技术后,你可以进一步学习各种前端框架和工具。</span><span class="sentence">React、Vue和Angular是目前最流行的前端框架。</span><span class="sentence">它们都采用了组件化的开发模式,提高了代码的可维护性和复用性。</span></p><p class="paragraph"><span class="sentence">除了前端技术,后端开发也是全栈工程师必须掌握的技能。</span><span class="sentence">Node.js让JavaScript可以用于服务器端编程,大大扩展了JavaScript的应用范围。</span><span class="sentence">数据库技术也是开发中的重要组成部分。</span></p>
</div><div class="progress-info">当前进度: <span id="progressText">0/0</span><div class="progress-bar-container"><div class="progress-bar"></div></div>
</div><script>document.addEventListener('DOMContentLoaded', function() {// 获取DOM元素const sentences = document.querySelectorAll('.sentence');const playBtn = document.getElementById('playBtn');const pauseBtn = document.getElementById('pauseBtn');const resumeBtn = document.getElementById('resumeBtn');const stopBtn = document.getElementById('stopBtn');const resetBtn = document.getElementById('resetBtn');const voiceSelect = document.getElementById('voiceSelect');const progressText = document.getElementById('progressText');const progressBar = document.querySelector('.progress-bar');// 语音合成相关变量let speechSynthesis = window.speechSynthesis;let voices = [];let currentUtterance = null;let currentSentenceIndex = 0;let isPaused = false;// 从本地存储加载进度loadProgress();// 初始化语音合成function initSpeechSynthesis() {// 获取可用的语音列表voices = speechSynthesis.getVoices();// 填充语音选择下拉框voiceSelect.innerHTML = '';voices.forEach((voice, index) => {const option = document.createElement('option');option.value = index;option.textContent = `${voice.name} (${voice.lang})`;voiceSelect.appendChild(option);});// 尝试选择中文语音const chineseVoice = voices.find(voice =>{voice.lang.includes('zh') || voice.lang.includes('cmn')});if (chineseVoice) {const voiceIndex = voices.indexOf(chineseVoice);voiceSelect.value = voiceIndex;}}// 语音列表加载可能需要时间speechSynthesis.onvoiceschanged = initSpeechSynthesis;initSpeechSynthesis();// 朗读指定句子function speakSentence(index) {if (index >= sentences.length || index < 0) return;// 停止当前朗读if (currentUtterance) {speechSynthesis.cancel();}// 更新当前句子高亮updateHighlight(index);// 创建新的语音合成实例const selectedVoiceIndex = voiceSelect.value;const utterance = new SpeechSynthesisUtterance(sentences[index].textContent);if (voices[selectedVoiceIndex]) {utterance.voice = voices[selectedVoiceIndex];}utterance.rate = 0.9; // 稍微慢一点的语速// 朗读开始时的处理utterance.onstart = function() {sentences[index].classList.add('reading');playBtn.disabled = true;pauseBtn.disabled = false;resumeBtn.disabled = true;stopBtn.disabled = false;};// 朗读结束时的处理utterance.onend = function() {sentences[index].classList.remove('reading');if (!isPaused) {if (currentSentenceIndex >= sentences.length - 1) {// 朗读完成playBtn.disabled = false;pauseBtn.disabled = true;resumeBtn.disabled = true;stopBtn.disabled = true;updateProgressText();return;}currentSentenceIndex++;saveProgress();speakSentence(currentSentenceIndex);}};// 开始朗读currentUtterance = utterance;speechSynthesis.speak(utterance);updateProgressText();}// 更新句子高亮function updateHighlight(index) {sentences.forEach((sentence, i) => {sentence.classList.remove('current');if (i === index) {sentence.classList.add('current');// 滚动到当前句子sentence.scrollIntoView({ behavior: 'smooth', block: 'center' });}});}// 更新进度文本function updateProgressText() {progressText.textContent = `${currentSentenceIndex + 1}/${sentences.length}`;const percentage = (currentSentenceIndex + 1) / sentences.length * 100;progressBar.style.width = `${percentage}%`;}// 保存进度到本地存储function saveProgress() {localStorage.setItem('readingProgress', currentSentenceIndex);localStorage.setItem('articleId', 'demoArticle'); // 在实际应用中可以使用文章IDupdateProgressText();}// 从本地存储加载进度function loadProgress() {const savedArticleId = localStorage.getItem('articleId');if (savedArticleId === 'demoArticle') {const savedProgress = localStorage.getItem('readingProgress');if (savedProgress !== null) {currentSentenceIndex = parseInt(savedProgress);if (currentSentenceIndex >= sentences.length) {currentSentenceIndex = 0;}updateHighlight(currentSentenceIndex);updateProgressText();}}}// 事件监听器playBtn.addEventListener('click', function() {currentSentenceIndex = 0;speakSentence(currentSentenceIndex);});pauseBtn.addEventListener('click', function() {if (speechSynthesis.speaking && !isPaused) {speechSynthesis.pause();isPaused = true;pauseBtn.disabled = true;resumeBtn.disabled = false;}});resumeBtn.addEventListener('click', function() {if (isPaused) {speechSynthesis.resume();isPaused = false;pauseBtn.disabled = false;resumeBtn.disabled = true;}});stopBtn.addEventListener('click', function() {speechSynthesis.cancel();isPaused = false;playBtn.disabled = false;pauseBtn.disabled = true;resumeBtn.disabled = true;stopBtn.disabled = true;// 移除所有朗读样式sentences.forEach(sentence => {sentence.classList.remove('reading');});});resetBtn.addEventListener('click', function() {localStorage.removeItem('readingProgress');localStorage.removeItem('articleId');currentSentenceIndex = 0;updateHighlight(currentSentenceIndex);updateProgressText();});// 点击句子跳转到该句子并朗读sentences.forEach((sentence, index) => {sentence.addEventListener('click', function() {currentSentenceIndex = index;speakSentence(currentSentenceIndex);});});});
</script>
</body>
</html>
相关文章:

使用 HTML + JavaScript 实现文章逐句高亮朗读功能
在这个信息爆炸的时代,我们每天都要面对大量的文字阅读。无论是学习、工作还是个人成长,阅读都扮演着至关重要的角色。然而,在快节奏的生活中,我们往往难以找到足够的安静时间专注于阅读。本文用 HTML JavaScript 实现了一个基于…...
【CSS-4】掌握CSS文字样式:从基础到高级技巧
文字是网页内容的核心载体,良好的文字样式设计不仅能提升可读性,还能增强网站的整体视觉效果。本文将全面介绍CSS中控制文字样式的各种属性和技巧,帮助您打造专业级的网页排版。 1. 基础文字属性 1.1 字体设置 (font-family) body {font-f…...

双碳时代,能源调度的难题正从“发电侧”转向“企业侧”
安科瑞刘鸿鹏 摘要 在“双碳”战略和能源结构转型的大背景下,企业储能电站逐步成为提升能源利用效率、增强用能韧性的重要手段。随着系统规模扩大与运行复杂度提升,如何对光伏、储能、负荷等流进行实时调控,成为智慧用能的关键。ACCU100微…...

3. 简述node.js特性与底层原理
😺😺😺 一、Node.js 底层原理(简化版) Node.js 是一个 基于 Chrome V8 引擎构建的 JavaScript 运行时,底层核心由几部分组成: 组成部分简要说明 1.V8 引擎 将 JS 编译成机器码执行࿰…...

OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 创建一个用于在 GPU 上执行模板匹配的 TemplateMatching 对象。 该函数返回一个指向 TemplateMatching 的智能指针(Ptr)…...

【Kubernetes】K8s 之 ETCD - 恢复备份
ETCD 是一个高可用的分布式键值存储,常用于存储配置信息和服务发现等。当系统出现故障或数据损坏时,能够快速恢复成先前的状态是维护系统稳定性的关键。ETCD 提供了备份和恢复功能,以确保数据持久性和可靠性,一起来看看如何操作吧…...
CMS、G1、ZGC、Shenandoah 的全面对比
大家好,这里是架构资源栈!点击上方关注,添加“星标”,一起学习大厂前沿架构! Java 垃圾回收器从最早的 Serial 一步步演化,如今已经有了多款高性能、低延迟的 GC 垃圾收集器可选,比如 CMS、G1、…...

RabbitMQ 学习
MQ 的相关概念 什么是 MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是 message 而已,还是一种跨进程的通信机制,用于上下游传递消息。…...

如何轻松、安全地管理密码(新手指南)
很多人会为所有账户使用相同、易记的密码,而且常常多年不换。虽然这样方便记忆,但安全性非常低。 您可能听说过一些大型网站的信息泄露事件,同样的风险也可能存在于您的WordPress网站中。如果有不法分子获取了访问权限,您的网站和…...

AWS App Mesh实战:构建可观测、安全的微服务通信解决方案
摘要:本文详解如何利用AWS App Mesh统一管理微服务间通信,实现精细化流量控制、端到端可观测性与安全通信,提升云原生应用稳定性。 一、什么是AWS App Mesh? AWS App Mesh 是一种服务网格(Service Mesh)解…...

9.axios底层原理,和promise的对比(2)
😺😺😺 和promise的对比 完全可以直接使用 Promise 来发 HTTP 请求,比如用原生 fetch Promise 就可以实现网络请求功能👇 ✅ 用 Promise fetch 的写法(原生) fetch(‘https://api.example.c…...

用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战 引言 在Web交互设计中,粒子动画因其动态美感和视觉吸引力被广泛应用于节日特效、情感化界面等场景。本文将通过实战案例,详细讲解如何使用HTML5 Canvas和JavaScript实现一个「心之律…...
Gartner《How to Create and Maintain a Knowledge Base forHumans and AI》学习报告
核心观点 本研究是一份 Gartne关于如何创建和维护面向人类与人工智能(AI)的知识库的研究报告。报告强调了知识库在知识管理(KM)中的核心地位,尤其是在生成式人工智能(GenAI)时代,一个结构良好的知识库是知识管理成功的关键,反之则可能成为整个知识管理实践的失败点。…...

【软件工具】批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项
批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项 1、操作步骤1-5: 安装与启动:安装成功后,在桌面或开始菜单找到软件图标,双击启动。 导入图片:进入软件主界面,点击 “导入图片” 按钮&a…...
PyTorch 中cumprod函数计算张量沿指定维度的累积乘积详解和代码示例
torch.cumprod 是 PyTorch 中用于 计算张量沿指定维度的累积乘积(cumulative product) 的函数。 1、函数原型 torch.cumprod(input, dim, *, dtypeNone, outNone) → Tensor参数说明: 参数说明input输入张量dim累积乘积的维度dtype可选&…...
docker镜像下载到本地,并导入服务器
应用场景 : 本地环境可以连接外网,但服务器连接不了外网,直接用docker pull 命令执行拉起镜像报异常。 1.本地拉取xuxueli/xxl-job-admin:2.2.0及查看所有下载的镜像 docker pull xuxueli/xxl-job-admin:2.2.0 docker images 2.保存镜像到…...

数据通信与计算机网络——数字传输
主要内容 数字到数字转换 线路编码 线路编码方案 块编码 扰动 模拟到数字转换 脉冲码调制(PCM) Delta调制(DM) 传输模式 并行传输 串行传输 一、数字到数字转换 将数字数据转换为数字信号涉及三种技术: 线…...
oracle 归档日志与RECOVERY_FILE_DEST 视图
1. RECOVERY_FILE_DEST 视图的作用 RECOVERY_FILE_DEST 是 Oracle 数据库用于 管理快速恢复区(Fast Recovery Area, FRA) 的一个视图。FRA 是 Oracle 提供的一种集中存储恢复相关文件(如归档日志、备份文件、闪回日志等)的区域。…...

黄柏基因组-小檗碱生物合成的趋同进化-文献精读142
Convergent evolution of berberine biosynthesis 小檗碱生物合成的趋同进化 摘要 小檗碱是一种有效的抗菌和抗糖尿病生物碱,主要从不同植物谱系中提取,特别是从小檗属(毛茛目,早期分支的真双子叶植物)和黄柏属&…...

前端杂货铺——TodoList
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…...

Spring Boot SSE流式输出+AI消息持久化升级实践:从粗暴到优雅的跃迁
在 AI 应用落地过程中,我们常常需要将用户和 AI 的对话以“完整上下文”的形式持久化到数据库中。但当 AI 回复非常长,甚至接近上万字时,传统的单条消息保存机制就会出问题。 在本篇文章中,我将深入讲解一次实际项目中对 对话持久…...
camera功能真的那么难用吗
背景 Android开发工作过程中,经常需要用到camera相关能力,比如:人脸识别,ai识别,拍照预览,摄像头录制等等需求。都需要使用到camera,且需要拿到camera的预览数据。但是每次开发这块代码都比较繁…...

Model Context Protocol (MCP) 是一个前沿框架
微软发布了 Model Context Protocol (MCP) 课程:mcp-for-beginners。 Model Context Protocol (MCP) 是一个前沿框架,涵盖 C#、Java、JavaScript、TypeScript 和 Python 等主流编程语言,规范 AI 模型与客户端应用之间的交互。 MCP 课程结构 …...
SQL Server 日期时间类型全解析:从精确存储到灵活转换
SQL Server 日期时间类型全解析:从精确存储到灵活转换 一、引言:日期时间处理的核心挑战 在数据管理中,日期时间类型是最常用却最容易出错的数据类型之一。不同业务场景对时间精度、时区感知、存储效率的需求差异极大: 金融交易…...
Android Test3 获取的ANDROID_ID值不同
Android Test3 获取的ANDROID_ID值不同 这篇文章来说明上一篇文章中说到的一个现象:在同一个项目中,创建不同的 app module,运行同一段测试代码,获取到的 ANDROID_ID 的值不同。 我也是第一次认真研究这个现象,这个还…...
[蓝桥杯 2024 国 B] 立定跳远
问题描述 在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 n 个检查点 a1,a2,...,an且 ai≥ai−1>0。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时,小明可以自行再增加 m 个检查点让自己跳得更轻松。在运动会前…...

内容力重塑品牌增长:开源AI大模型驱动下的智能名片与S2B2C商城赋能抖音生态种草范式
摘要:内容力已成为抖音生态中品牌差异化竞争的核心能力,通过有价值、强共鸣的内容实现产品"种草"与转化闭环。本文基于"开源AI大模型AI智能名片S2B2C商城小程序源码"技术架构,提出"技术赋能内容"的新型种草范式…...

手机号在网状态查询接口如何用PHP实现调用?
一、什么是手机号在网状态查询接口 通过精准探测手机号的状态,帮助平台减少此类问题的发生,提供更个性化的服务或进行地域性营销 二、应用场景 1. 金融风控 通过运营商在网态查询接口,金融机构可以核验贷款申请人的手机状态,拦…...

【Java微服务组件】分布式协调P4-一文打通Redisson:从API实战到分布式锁核心源码剖析
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。 目录 引言Redisson基本信息Redisson网站 Redisson应用…...

一个简单的德劳内三角剖分实现
德劳内(Delaunay)三角剖分是一种经典的将点集进行三角网格化预处理的手段,在NavMesh、随机地牢生成等场景下都有应用。 具体内容百度一大堆,就不介绍了。 比较知名的算法是Bowyer-Watson算法,也就是逐点插入法。 下雨闲…...