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 } = Vue


        const useVirtualList = (options) => {
            const { rowHeight, height, dataSource, columnCount = 1 } = options
            const 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.scroll
                const rowKey = props.rowKey
                const 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.volume
                        utterThis.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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu 18.04安装自己ko驱动 修改secure boot

因为本人老折腾自己的电脑&#xff0c;所以老重装系统&#xff0c;然后配置又不见了&#xff0c;这次配置赶紧记下来 insmod netlink_test.ko 报错&#xff1a;insmod: ERROR: could not insert module netlink_test.ko: Operation not permitted 添加 sudo insmod netlink_te…

XCTF刷题十一道(01)

文章目录 Training-WWW-RobotsPHP2unserialize3view-sourceget_postrobotsbackupcookiedisabled_buttonweak_authsimple_php Training-WWW-Robots robots.txt&#xff0c;防爬虫&#xff0c;访问urlrobots.txt PHP2 phps源码泄露 >phps文件就是php的源代码文件&#xff0…

swift语言用哪种库适合做爬虫?

目录 1、Alamofire 2、URLSession 3、YepHttp 4、Kickbox 5、Vapor 注意事项 总结 在Swift语言中&#xff0c;可以使用第三方库来帮助进行网络爬虫的开发。以下是几个适合Swift语言使用的爬虫库&#xff0c;以及相应的代码示例&#xff1a; 1、Alamofire Alamofire是Sw…

【k8s】pod控制器

一、pod控制器及其功用 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类 自主式Pod&#xff1a; kubernetes直接创建出来的Pod&#xff0c;这种Pod删除后就没有了&#xff0c;也不会重建 控制器创建的Pod&#xff1a…

润和软件HopeStage与奇安信网神终端安全管理系统、可信浏览器完成产品兼容性互认证

近日&#xff0c;江苏润和软件股份有限公司&#xff08;以下简称“润和软件”&#xff09;HopeStage 操作系统与奇安信网神信息技术&#xff08;北京&#xff09;股份有限公司&#xff08;以下简称“奇安信”&#xff09;终端安全管理系统、可信浏览器完成产品兼容性测试。 测试…

多路转接(上)——select

目录 一、select接口 1.认识select系统调用 2.对各个参数的认识 二、编写select服务器 1.两个工具类 2.网络套接字封装 3.服务器类编写 4.源文件编写 5.运行 一、select接口 1.认识select系统调用 int select(int nfds, fd_set readfds, fd_set writefds, fd_set ex…

Node.js |(六)express框架 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;express使用&#x1f407;初体验&#x1f407;express路由⭐️路由的使用⭐️获取请求参数⭐️获取路由参数&#x1f525;练习&#xff1a;根据路由参数响应歌手信息…

小白学爬虫:通过关键词搜索1688商品列表数据接口|1688商品列表数据接口|1688商品列表数据采集|1688API接口

通过关键词搜索1688商品列表数据接口可以使用1688开放平台提供的API接口实现。以下是使用关键词搜索商品列表数据的基本步骤&#xff1a; 1、注册并获取AppKey。 2、构造请求参数&#xff0c;包括搜索关键词、页码、每页条数等。 3、通过API接口链接&#xff0c;将请求参数发送…

简单漂亮的登录页面

效果图 说明 开发环境&#xff1a;vue3&#xff0c;sass 代码 <template><div class"container"><div class"card-container"><div class"card-left"><span><h1>Dashboard</h1><p>Lorem ip…

后台管理系统解决方案-中大型-Vben Admin

后台管理系统解决方案-中大型-Vben Admin 官网 Vben Admin 在线演示 Vben Admin 为什么选择它 github现有20K星&#xff0c;并且它有个可视化生成表单&#xff0c;我很喜欢 快速开始 # 拉取代码 git clone https://github.com/vbenjs/vue-vben-admin-doc# 安装依赖 yarn#…

【ONE·C++ || 网络基础(二)】

总言 主要内容&#xff1a;演示socke套接字编程&#xff08;TCP模式&#xff09;&#xff0c;介绍序列化和反序列化&#xff0c;并进行演示&#xff08;json版本达成协议编写、守护进程介绍&#xff09;。 文章目录 总言4、基于套接字的TCP网络程序4.0、log.hpp4.1、version1.…

C++中将数据添加到文件的末尾

参考:https://blog.csdn.net/qq_23880193/article/details/44279283 C中文件的读取需要包含fstream文件&#xff0c;即&#xff1a;#include 文件的读取和写入是是通过流操作来的&#xff0c;这不像输入、输出流那样&#xff0c;库中已经定义了对象cin和cout 文件的读取需要声…

【 毕设项目源码推荐 javaweb 项目】 基于 springboot+vue 的图书个性化推荐系统的设计与实现(springboot003)

简介 :::warning 【 毕设项目源码推荐 javaweb 项目】 基于 springbootvue 的图书个性化推荐系统的设计与实现适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负…

Etcd 常用命令与备份恢复

1. etcd简介 官方网站&#xff1a;etcd.io 官方文档&#xff1a;etcd.io/docs/v3.5/op-guide/maintenance 官方硬件推荐&#xff1a;etcd.io/docs/v3.5/op-guide/hardware github地址&#xff1a;github.com/etcd-io/etcd etcd是CoreOS团队于2013年6月发起的开源项目&#xf…

算法--数据结构

这里写目录标题 本节内容链表与邻接表链表主要思想链表操作初始化在head结点后面插入普通插入删除操作 例子 双链表&#xff08;双向循环链表&#xff09;主要思想操作初始化双向插入删除第k个点 邻接表主要思想 栈和队列栈主要思想主要操作 队列主要思想操作 单调栈与单调队列…

Kafka中遇到的错误:

1、原因&#xff1a;kafka是一个去中心化结果的&#xff0c;所以在启动Kafka的时候&#xff0c;每一个节点上都需要启动。 启动的命令&#xff1a;kafka-server-start.sh -daemon /usr/local/soft/kafka_2.11-1.0.0/config/server.properties

【凡人修仙传】预计开播倒计时,线下举办超前观影活动,隆重期待

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析国漫资讯。 深度爆料凡人最新资讯&#xff0c;《凡人修仙传》这部备受期待的动漫作品&#xff0c;终于在新年之际宣布了定档日期。据悉&#xff0c;该动漫将于11月25日&#xff0c;也就是周六上午11点&#xff0c;与广大…

【C++干货铺】STL简述 | string类的使用指南

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 什么是STL STL的版本 STL的六大组件 STL的缺陷 string类 C语言中的字符串 标准库中的string类 string类常用的接口使用指南 string类中常见的构造 strin…

全国5米高程DEM数据及衍生的坡度数据

坡度是地表单元陡缓的程度&#xff0c;通常把坡面的垂直高度和水平距离的比值称为坡度。坡度的表示方法有百分比法、度数法、密位法和分数法四种&#xff0c;其中以百分比法和度数法较为常用。 坡度是地表单元陡缓的程度&#xff0c;通常把坡面的垂直高度和水平距离的比值称为坡…

Jmeter 性能压测 —— 混合场景

性能测试&#xff0c;单场景的目的一般是为了发现缺陷、发现瓶颈。 完成所有单个重点场景的性能测试之后&#xff0c;还需要做一个混合场景的性能测试-评估系统整体性能。 1、场景设计 使用Jmeter 做混合场景设计 在一个测试计划&#xff0c;将每个重点测试场景各创建为一个…