Vue3 简单实现虚拟Table,展示海量单词.利用WebAPI speechSynthesis,朗读英语单词

目录

本页面完整代码

视频演示

完整的页面代码


利用webapi speechSynthesis帮助我们自动郎读英语单词,可以利用这个API,做一些小说朗读或到账提示。

 

本页面完整代码

用Vue写了一个简单页面,里面还写了一个简单的虚拟Table支持海量数据展示。

视频演示

20231106-223410

完整的页面代码

里面的all.js文件是英语四级的单词,在文章内自行下载,也可以去这里面把JSON下载。

GitHub - cuttlin/Vocabulary-of-CET-4: 英语四级词库

复制里面的代码,放到html文件就可以运行

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="all.js"></script><script src="https://cdn.jsdelivr.net/npm/vue@3.3.7/dist/vue.global.js"></script><style>body{background-color: rgba(0,0,0,0.04);}.table-wrapper{background-color: #fff;border: solid 1px #efefef;box-shadow: 0 0px 3px 1px rgba(0,0,0,0.05);}.table-wrapper table {width: 100%;border-spacing: 0;table-layout: fixed;}.header-table th {background-color: #00a674;height: 40px;line-height: 40px;color: rgb(158, 255, 205);}.body-table td {background-color: #fff;text-align: center;}.body-table tr:nth-of-type(n+2) td {border-top: solid 1px rgba(0, 0, 0, 0.06);}.body-table tr:hover td {background-color: #f7f7f7;}.form-wrap{background-color: #fff;margin-bottom: 15px;padding: 15px;box-shadow: 0 1px 3px 1px rgba(0,0,0,0.05);}.table-form {table-layout:fixed;}.table-form th,.table-form td{height: 25px;line-height: 25px;}.table-form th{width: 80px;font-weight: 400;font-size: 14px;text-align: right;}.table-form th::after{content:':';}</style>
</head>
<body><div id="app"></div><template id="tplApp"><div><div class="form-wrap"><table class="table-form"><tr><th>声音</th><td colspan="5"> <select v-model="voice.lang"><option v-for="(v,i) in voices" :key="v.key" :value="v.name">{{v.name}}</option></select></td></tr><tr><th>语速</th><td><input v-model.number="voice.rate" type="number" min="0.1" max="10" step="0.1"/></td><th>音调</th><td><input v-model.number="voice.pitch" type="number" min="0" max="2" step="0.1"/></td><th>音量</th><td><input v-model.number="voice.volume" type="number" min="0" max="1" step="0.1"/></td></tr></table></div></div><Virtual-Table :columns="columns" :data-source="dataSource" row-key="word" :row-height="50" :scroll="scroll"></VirtualTable></div></template><script>const { ref, shallowRef, h, toRaw, renderSlot,reactive,shallowReactive, toRefs, toRef, computed } = Vueconst useVirtualList = (options) => {const { rowHeight, height, dataSource, columnCount = 1 } = optionsconst scrollTop = ref(0)const onScroll = (e) => {scrollTop.value = e.target.scrollTop}const scrollRowIndex = computed(() => {return Math.floor(scrollTop.value / rowHeight.value)})const visibilityRowCount = computed(() => {return Math.ceil(height / rowHeight.value)})const start = computed(() => {return scrollRowIndex.value * columnCount})const end = computed(() => {return start.value + visibilityRowCount.value * columnCount})const rowCount = computed(() => {return Math.ceil(dataSource.value.length / columnCount)})const scrollHeight = computed(() => {return rowCount.value * rowHeight.value})const currentList = computed(() => {return dataSource.value.slice(start.value, end.value)})const containerProps = computed(() => {return {style: {height: height + 'px',overflowY: 'auto'},onScroll: onScroll}})const invisibleHeight = computed(() => {return (scrollRowIndex.value * rowHeight.value)})const scrollProps = computed(() => {return {style: {height: scrollHeight.value + 'px',paddingTop: invisibleHeight.value + 'px',boxSizing: 'border-box',},}})return [{containerProps,scrollProps,data: currentList}]}const VirtualTable = {props: ['columns', 'rowKey', 'dataSource', 'scroll', 'rowHeight'],setup(props, { slots }) {const rowHeight = toRef(props, 'rowHeight')console.log('rowHeight',rowHeight.value)const scroll = props.scrollconst rowKey = props.rowKeyconst columns = toRef(props, 'columns')const dataSource = toRef(props, 'dataSource')const [{ containerProps, scrollProps, data: currentData }] = useVirtualList({rowKey: rowKey,rowHeight: rowHeight,height: scroll.y,dataSource: dataSource})const renderCol = (columns) => {return h('colgroup', {}, columns.map((c, i) => {return h('col', {key: c.dataIndex || i,style: {...(c.width ? { width: c.width + 'px' } : {})}})}))}const renderHeader = (columns) => {return h('thead', {}, h('tr', {}, columns.map((c, i) => {return h('th', {key: c.dataIndex || i,}, c.title)})))}const renderCell = (columns, dataItem) => {return columns.map((c, i) => {return h('td', {key: c.dataIndex || i,}, c.render ? c.render(dataItem[c.dataIndex], dataItem, i) : dataItem[c.dataIndex])})}const renderRow = (data) => {return h('tbody', {}, data.map((d, i) => {return h('tr', {key: d[rowKey],style: {height: rowHeight.value + 'px'}}, renderCell(columns.value, d))}))}return () => {return h('div', {class: 'table-wrapper'},h('div', {class: 'header-wrap'}, h('table', {class: 'header-table'},renderCol(columns.value),renderHeader(columns.value),)),h('div', {class: 'body-wrap',...containerProps.value}, h('div', {class: 'body-scroll-wrap',...scrollProps.value},h('table', {class: 'body-table'},renderCol(columns.value),renderRow(currentData.value)))))}}}const app = Vue.createApp({template: '#tplApp',components:{VirtualTable:VirtualTable},setup() {const voices=shallowRef([])const voice=shallowReactive({lang:"",pitch:1,rate:1,volume:1})speechSynthesis.addEventListener('voiceschanged', () => {voices.value = speechSynthesis.getVoices()voice.lang=voices.value[0].name})// 语音合成const speak=(word, options = {})=> {return new Promise((resolve, reject) => {const utterThis = new SpeechSynthesisUtterance(word);for (let i = 0; i < voices.value.length; i++) {if ( voices.value[i].name === voice.lang) {utterThis.voice =  voices.value[i];}}utterThis.pitch = voice.pitch;utterThis.rate = voice.rate;utterThis.volume = voice.volumeutterThis.onend = function () {resolve()}utterThis.onerror = (e) => {reject(e)}speechSynthesis.speak(utterThis);})}const columns = shallowRef([{title: '单词',dataIndex: 'word',width: 220},{title: '音标',dataIndex: 'phonetic_symbol',width: 220},{title: '中文意思',dataIndex: 'mean'},{title: '操作',width: 160,render(v, record) {return h('div',{},h('button', {onClick: () => {speak(record.word)}}, '朗读单词'),h('button', {style:{marginLeft:'5px'},onClick: () => {speak(record.mean)}}, '朗读中文'))}}])const dataSource = shallowRef(english_word_cet4_all)return {voices,voice,dataSource,columns:columns,scroll:{y:window.innerHeight-150}}}})app.mount('#app')</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/162790.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

记录:Unity脚本的编写5.0

目录 前言创建动画Unity Animation、Animator常用类关于两者的区别Animator 编写脚本 大型连续剧之在untiy中&#xff08;或者别的什么活动&#xff09; 前言 之前在场景中添加了背景音乐&#xff0c;而在我们的日常的体验中&#xff0c;可以发现游戏或者场景中有很多有趣的动…

pyspark连接mysql数据库报错

使用pyspark连接mysql数据库代码如下 spark_conf SparkConf().setAppName("MyApp").setMaster("local")spark SparkSession.builder.config(confspark_conf).getOrCreate()url "jdbc:mysql://localhost:3306/test?useUnicodetrue&characterE…

MFC 基础篇(一)

目录 一.SDK编程 二.为什么要学MFC&#xff1f; 三.MFC能做什么&#xff1f; 四.MFC开发环境搭建 五.MFC项目创建 六.消息映射机制 一.SDK编程 Application Programming Interface 应用程序编程接口。 Software Development Kit 软件开发工具包&#xff0c;一般会包括A…

【音视频 | opus】opus编码的Ogg封装文件详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Android Gldie复用只取之前decode过的缓存resource,Kotlin

Android Gldie复用只取之前decode过的缓存resource&#xff0c;Kotlin import android.graphics.Bitmap import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.life…

ARMday1

1、计算机的组成 输入设备-输出设备-运算器-控制器-存储器 输入设备&#xff1a;键盘、鼠标、手柄、扫描仪 输出设备&#xff1a;显示屏、打印机、音响 存储器&#xff1a;存放数据以及指令、是实现“程序存储控制”的基础、外存、内存、cache、寄存器 控制器&#xff08;…

梳理自动驾驶中的各类坐标系

目录 自动驾驶中的坐标系定义 关于坐标系的定义 几大常用坐标系 世界坐标系 自车坐标系 传感器坐标系 激光雷达坐标系 相机坐标系 如何理解坐标转换 机器人基础中的坐标转换概念 左乘右乘的概念 对左乘右乘的理解 再谈自动驾驶中的坐标转换 本节参考文献 自动驾驶…

Android Datastore 动态创建与源码解析

涉及到的知识点 1、协程原理---->很好的博客介绍&#xff0c;一个小故事讲明白进程、线程、Kotlin 协程到底啥关系&#xff1f; 2、Channel知识点---->Android—kotlin-Channel超详细讲解 3、Coroutines : CompletableDeferred and structured concurrency 封装的DataS…

基于Chirp窄带扩频技术的无线混合组网应用,以多角色智能计量插座作为Chirp广域基站,构建边缘计算混合无线网络

随着物联网&#xff08;IoT&#xff09;的不断发展&#xff0c;无线通信技术的需求也在不断增加。Chirp窄带扩频技术是一种具有广泛应用潜力的无线通信技术&#xff0c;它在低功耗、广域覆盖、抗干扰等方面具备独特的优势。本文介绍了如何利用磐启微Chirp技术构建ECWAN无线混合…

卡尔曼滤波之二:Python实现

卡尔曼滤波之二&#xff1a;Python实现 1.背景描述2.构建卡尔曼滤波公式2.1 预测2.2 更新 3.代码实现3.1 输入值3.2 pykalman包实现3.3 不使用Python包实现3.4 效果可视化 参考文献 了解了卡尔曼滤波之一&#xff1a;基本概念&#xff0c;可以结合代码来理解下卡尔曼滤波的2个预…

【数据库】数据库模式 Schema

数据库模式 Schema 1.MySQL2.PostgreSQL3.SQL Server4.Oracle5.SQLite 在数据库的术语中&#xff0c;模式&#xff08;schema&#xff09;是一个逻辑概念&#xff0c;用于组织数据库中的对象。模式中的对象通常包括 表、索引、数据类型、序列、视图、存储过程、主键、外键 等等…

Electron[3] 基础配置准备和Electron入门案例

1 背景 上一篇文章已经分享了&#xff0c;如何准备Electron的基础环境了。但是博客刚发才一天&#xff0c;就发现有人问问题了。经过实践发现&#xff0c;严格按照作者的博客教程走是不会有问题的&#xff0c;其中包括安装的环境版本等都要一致。因为昨天发的博客&#xff0c;…