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


    目录

    本页面完整代码

    视频演示

    完整的页面代码


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

     

    本页面完整代码

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

    视频演示

    20231106-223410

    完整的页面代码

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

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

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>Documenttitle>
    7. <script src="all.js">script>
    8. <script src="https://cdn.jsdelivr.net/npm/vue@3.3.7/dist/vue.global.js">script>
    9. <style>
    10. body{
    11. background-color: rgba(0,0,0,0.04);
    12. }
    13. .table-wrapper{
    14. background-color: #fff;
    15. border: solid 1px #efefef;
    16. box-shadow: 0 0px 3px 1px rgba(0,0,0,0.05);
    17. }
    18. .table-wrapper table {
    19. width: 100%;
    20. border-spacing: 0;
    21. table-layout: fixed;
    22. }
    23. .header-table th {
    24. background-color: #00a674;
    25. height: 40px;
    26. line-height: 40px;
    27. color: rgb(158, 255, 205);
    28. }
    29. .body-table td {
    30. background-color: #fff;
    31. text-align: center;
    32. }
    33. .body-table tr:nth-of-type(n+2) td {
    34. border-top: solid 1px rgba(0, 0, 0, 0.06);
    35. }
    36. .body-table tr:hover td {
    37. background-color: #f7f7f7;
    38. }
    39. .form-wrap{
    40. background-color: #fff;
    41. margin-bottom: 15px;
    42. padding: 15px;
    43. box-shadow: 0 1px 3px 1px rgba(0,0,0,0.05);
    44. }
    45. .table-form {
    46. table-layout:fixed;
    47. }
    48. .table-form th,.table-form td{
    49. height: 25px;
    50. line-height: 25px;
    51. }
    52. .table-form th{
    53. width: 80px;
    54. font-weight: 400;
    55. font-size: 14px;
    56. text-align: right;
    57. }
    58. .table-form th::after{
    59. content:':';
    60. }
    61. style>
    62. head>
    63. <body>
    64. <div id="app">
    65. div>
    66. <template id="tplApp">
    67. <div>
    68. <div class="form-wrap">
    69. <table class="table-form">
    70. <tr>
    71. <th>声音th>
    72. <td colspan="5"> <select v-model="voice.lang">
    73. <option v-for="(v,i) in voices" :key="v.key" :value="v.name">{{v.name}}option>
    74. select>td>
    75. tr>
    76. <tr>
    77. <th>语速th>
    78. <td><input v-model.number="voice.rate" type="number" min="0.1" max="10" step="0.1"/>td>
    79. <th>音调th>
    80. <td><input v-model.number="voice.pitch" type="number" min="0" max="2" step="0.1"/>td>
    81. <th>音量th>
    82. <td><input v-model.number="voice.volume" type="number" min="0" max="1" step="0.1"/>td>
    83. tr>
    84. table>
    85. div>
    86. div>
    87. <Virtual-Table :columns="columns" :data-source="dataSource" row-key="word" :row-height="50" :scroll="scroll">VirtualTable>
    88. div>
    89. template>
    90. <script>
    91. const { ref, shallowRef, h, toRaw, renderSlot,reactive,shallowReactive, toRefs, toRef, computed } = Vue
    92. const useVirtualList = (options) => {
    93. const { rowHeight, height, dataSource, columnCount = 1 } = options
    94. const scrollTop = ref(0)
    95. const onScroll = (e) => {
    96. scrollTop.value = e.target.scrollTop
    97. }
    98. const scrollRowIndex = computed(() => {
    99. return Math.floor(scrollTop.value / rowHeight.value)
    100. })
    101. const visibilityRowCount = computed(() => {
    102. return Math.ceil(height / rowHeight.value)
    103. })
    104. const start = computed(() => {
    105. return scrollRowIndex.value * columnCount
    106. })
    107. const end = computed(() => {
    108. return start.value + visibilityRowCount.value * columnCount
    109. })
    110. const rowCount = computed(() => {
    111. return Math.ceil(dataSource.value.length / columnCount)
    112. })
    113. const scrollHeight = computed(() => {
    114. return rowCount.value * rowHeight.value
    115. })
    116. const currentList = computed(() => {
    117. return dataSource.value.slice(start.value, end.value)
    118. })
    119. const containerProps = computed(() => {
    120. return {
    121. style: {
    122. height: height + 'px',
    123. overflowY: 'auto'
    124. },
    125. onScroll: onScroll
    126. }
    127. })
    128. const invisibleHeight = computed(() => {
    129. return (scrollRowIndex.value * rowHeight.value)
    130. })
    131. const scrollProps = computed(() => {
    132. return {
    133. style: {
    134. height: scrollHeight.value + 'px',
    135. paddingTop: invisibleHeight.value + 'px',
    136. boxSizing: 'border-box',
    137. },
    138. }
    139. })
    140. return [{
    141. containerProps,
    142. scrollProps,
    143. data: currentList
    144. }]
    145. }
    146. const VirtualTable = {
    147. props: ['columns', 'rowKey', 'dataSource', 'scroll', 'rowHeight'],
    148. setup(props, { slots }) {
    149. const rowHeight = toRef(props, 'rowHeight')
    150. console.log('rowHeight',rowHeight.value)
    151. const scroll = props.scroll
    152. const rowKey = props.rowKey
    153. const columns = toRef(props, 'columns')
    154. const dataSource = toRef(props, 'dataSource')
    155. const [{ containerProps, scrollProps, data: currentData }] = useVirtualList({
    156. rowKey: rowKey,
    157. rowHeight: rowHeight,
    158. height: scroll.y,
    159. dataSource: dataSource
    160. })
    161. const renderCol = (columns) => {
    162. return h('colgroup', {}, columns.map((c, i) => {
    163. return h('col', {
    164. key: c.dataIndex || i,
    165. style: {
    166. ...(c.width ? { width: c.width + 'px' } : {})
    167. }
    168. })
    169. }))
    170. }
    171. const renderHeader = (columns) => {
    172. return h('thead', {}, h('tr', {}, columns.map((c, i) => {
    173. return h('th', {
    174. key: c.dataIndex || i,
    175. }, c.title)
    176. })))
    177. }
    178. const renderCell = (columns, dataItem) => {
    179. return columns.map((c, i) => {
    180. return h('td', {
    181. key: c.dataIndex || i,
    182. }, c.render ? c.render(dataItem[c.dataIndex], dataItem, i) : dataItem[c.dataIndex])
    183. })
    184. }
    185. const renderRow = (data) => {
    186. return h('tbody', {}, data.map((d, i) => {
    187. return h('tr', {
    188. key: d[rowKey],
    189. style: {
    190. height: rowHeight.value + 'px'
    191. }
    192. }, renderCell(columns.value, d))
    193. }))
    194. }
    195. return () => {
    196. return h('div', {
    197. class: 'table-wrapper'
    198. },
    199. h('div', {
    200. class: 'header-wrap'
    201. }, h('table', {
    202. class: 'header-table'
    203. },
    204. renderCol(columns.value),
    205. renderHeader(columns.value),
    206. )),
    207. h('div', {
    208. class: 'body-wrap',
    209. ...containerProps.value
    210. }, h('div', {
    211. class: 'body-scroll-wrap',
    212. ...scrollProps.value
    213. },
    214. h('table', {
    215. class: 'body-table'
    216. },
    217. renderCol(columns.value),
    218. renderRow(currentData.value))
    219. ))
    220. )
    221. }
    222. }
    223. }
    224. const app = Vue.createApp({
    225. template: '#tplApp',
    226. components:{
    227. VirtualTable:VirtualTable
    228. },
    229. setup() {
    230. const voices=shallowRef([])
    231. const voice=shallowReactive({
    232. lang:"",
    233. pitch:1,
    234. rate:1,
    235. volume:1
    236. })
    237. speechSynthesis.addEventListener('voiceschanged', () => {
    238. voices.value = speechSynthesis.getVoices()
    239. voice.lang=voices.value[0].name
    240. })
    241. // 语音合成
    242. const speak=(word, options = {})=> {
    243. return new Promise((resolve, reject) => {
    244. const utterThis = new SpeechSynthesisUtterance(word);
    245. for (let i = 0; i < voices.value.length; i++) {
    246. if ( voices.value[i].name === voice.lang) {
    247. utterThis.voice = voices.value[i];
    248. }
    249. }
    250. utterThis.pitch = voice.pitch;
    251. utterThis.rate = voice.rate;
    252. utterThis.volume = voice.volume
    253. utterThis.onend = function () {
    254. resolve()
    255. }
    256. utterThis.onerror = (e) => {
    257. reject(e)
    258. }
    259. speechSynthesis.speak(utterThis);
    260. })
    261. }
    262. const columns = shallowRef([
    263. {
    264. title: '单词',
    265. dataIndex: 'word',
    266. width: 220
    267. },
    268. {
    269. title: '音标',
    270. dataIndex: 'phonetic_symbol',
    271. width: 220
    272. },
    273. {
    274. title: '中文意思',
    275. dataIndex: 'mean'
    276. },
    277. {
    278. title: '操作',
    279. width: 160,
    280. render(v, record) {
    281. return h('div',{
    282. },h('button', {
    283. onClick: () => {
    284. speak(record.word)
    285. }
    286. }, '朗读单词'),h('button', {
    287. style:{
    288. marginLeft:'5px'
    289. },
    290. onClick: () => {
    291. speak(record.mean)
    292. }
    293. }, '朗读中文'))
    294. }
    295. }
    296. ])
    297. const dataSource = shallowRef(english_word_cet4_all)
    298. return {
    299. voices,
    300. voice,
    301. dataSource,
    302. columns:columns,
    303. scroll:{
    304. y:window.innerHeight-150
    305. }
    306. }
    307. }
    308. })
    309. app.mount('#app')
    310. script>
    311. body>
    312. html>

  • 相关阅读:
    k8s-NFS系统配置
    Ranger (五) --------- 使用 Ranger 对 Hive 进行权限管理
    选错毕业第一份工作,我白干半年
    【java基础面试题】jdk、jre、jvm区别
    Python中的函数
    求告知识图谱构建工具
    【51】分布式计算:如果所有人的大脑都联网会怎样?
    SAP 物料分类账配置详解Part 1( 基于SAP S/4HANA1909 版本)
    前端实现给文字添加动态背景
    pip某些包发生SSL错误
  • 原文地址:https://blog.csdn.net/long5305350/article/details/51500673