Voicefullscreen.vue 102 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247
  1. <!-- -->
  2. <template>
  3. <div :class="['voicefull', bgIndex == 0 ? 'bg1' : 'bg2']" v-loading="loading">
  4. <template v-if="sentList">
  5. <div class="voicefull-top">
  6. <!--
  7. @mouseover="setTopShow(true)"
  8. @mouseleave="setTopShow(false)"
  9. -->
  10. <div
  11. :class="[isTopShow ? 'voicefull-top-show' : 'voicefull-top-hidden']"
  12. >
  13. <div class="top-left" v-if="patternType != '录音模式'">
  14. <div :class="['select-bg', bgIndex == 1 ? 'select-bg-blue' : '']">
  15. <div :class="['bg-green-box', bgIndex == 1 ? 'active' : '']">
  16. <span
  17. :class="['bg-green', bgIndex == 1 ? 'active' : '']"
  18. @click="changeBg(1)"
  19. ></span>
  20. </div>
  21. <div :class="['bg-white-box', bgIndex == 0 ? 'active' : '']">
  22. <span
  23. :class="['bg-white', bgIndex == 0 ? 'active' : '']"
  24. @click="changeBg(0)"
  25. ></span>
  26. </div>
  27. </div>
  28. <div
  29. :class="[
  30. 'set-fontSize',
  31. bgIndex == 1 ? 'set-fontSize-green' : '',
  32. ]"
  33. >
  34. <template v-if="hzSize >= 34">
  35. <span
  36. :class="[
  37. 'font-jian-black',
  38. bgIndex == 1 ? 'font-jian-yellow' : '',
  39. ]"
  40. @click="setFontSize('-')"
  41. ></span>
  42. </template>
  43. <template v-else>
  44. <span
  45. :class="[
  46. 'font-jian-black',
  47. bgIndex == 1
  48. ? 'font-jian-yellow-disabled'
  49. : 'font-jian-white-disabled',
  50. ]"
  51. ></span>
  52. </template>
  53. <span
  54. :class="[
  55. 'font-img-black',
  56. bgIndex == 1 ? 'font-img-yellow' : '',
  57. ]"
  58. ></span>
  59. <template v-if="hzSize <= 76">
  60. <span
  61. :class="[
  62. 'font-jia-black',
  63. bgIndex == 1 ? 'font-jia-yellow' : '',
  64. ]"
  65. @click="setFontSize('+')"
  66. ></span>
  67. </template>
  68. <template v-else>
  69. <span
  70. :class="[
  71. 'font-jia-black',
  72. bgIndex == 1
  73. ? 'font-jia-yellow-disabled'
  74. : 'font-jia-white-disabled',
  75. ]"
  76. ></span>
  77. </template>
  78. </div>
  79. </div>
  80. <div class="top-middle">
  81. <template v-if="patternType != '录音模式'">
  82. <template v-if="mp3">
  83. <AudioLineSentence
  84. :key="'sent' + curSentIndex"
  85. :mp3="mp3"
  86. :getCurTime="getCurTime"
  87. ref="audioLineSent"
  88. :audioId="'artPraAudioId' + curSentIndex"
  89. :stopAudio="stopAudio"
  90. :width="120"
  91. :hideSlider="true"
  92. :bg="bg"
  93. :ed="ed"
  94. :curTime="curTime"
  95. :maxTime="maxTime"
  96. :bgIndex="bgIndex"
  97. :isRepeat="isRepeat"
  98. :isAuto="isAuto"
  99. @playChange="playChange"
  100. @rollSentence="rollSentence"
  101. />
  102. </template>
  103. <div
  104. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  105. @click="setStatus"
  106. >
  107. <span
  108. :class="[
  109. 'repeat-icon',
  110. !isRepeat && !isAuto ? 'disabled' : '',
  111. !isRepeat && isAuto ? 'auto-icon' : '',
  112. isRepeat && bgIndex == 1 ? 'repeat-icon-yellow' : '',
  113. !isRepeat && isAuto && bgIndex == 1
  114. ? 'auto-icon-yellow'
  115. : '',
  116. ]"
  117. ></span>
  118. </div>
  119. </template>
  120. <div
  121. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  122. @click="changeStatus('isKeyboard')"
  123. title="键盘控制开启后,可用方向键控制翻页,空格键播放暂停,回车键录音"
  124. >
  125. <span
  126. :class="[
  127. 'keyboard-icon',
  128. !isKeyboard ? 'disabled' : '',
  129. isKeyboard && bgIndex == 1 ? 'keyboard-icon-yellow' : '',
  130. ]"
  131. ></span>
  132. </div>
  133. <div
  134. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  135. @click="changePinyin"
  136. >
  137. <span
  138. :class="[
  139. 'pinyin-icon',
  140. !config.isShowPY ? 'disabled' : '',
  141. config.isShowPY && bgIndex == 1 ? 'pinyin-icon-yellow' : '',
  142. ]"
  143. ></span>
  144. </div>
  145. <div
  146. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  147. @click="changeEN"
  148. >
  149. <span
  150. :class="[
  151. 'en-icon',
  152. !enwords ? 'disabled' : '',
  153. !config.isShowEN ? 'disabled' : '',
  154. config.isShowEN && bgIndex == 1 ? 'en-icon-yellow' : '',
  155. ]"
  156. ></span>
  157. </div>
  158. <div
  159. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  160. @click="handleColl"
  161. title="点击收藏后可在“个人中心”-“我的收藏”查看"
  162. >
  163. <span
  164. :class="[
  165. 'coll-icon',
  166. !isCollArr[curSentIndex] ? 'disabled' : '',
  167. isCollArr[curSentIndex] && bgIndex == 1
  168. ? 'coll-icon-yellow'
  169. : '',
  170. ]"
  171. ></span>
  172. </div>
  173. </div>
  174. <div
  175. :class="['op-btn', bgIndex == 1 ? 'op-btn-green' : '']"
  176. @click="exitFullScreen"
  177. >
  178. <span
  179. :class="['close-icon', bgIndex == 1 ? 'close-icon-white' : '']"
  180. ></span>
  181. </div>
  182. </div>
  183. </div>
  184. <div class="voicefull-content" v-if="item">
  185. <div
  186. class="vc-box"
  187. @mousemove="showPrevNext(true, 'isShowLeft')"
  188. @mouseleave="showPrevNext(false, 'isShowLeft')"
  189. >
  190. <div
  191. :class="[
  192. 'vc-left vc-left-grey',
  193. isShowLeft && bgIndex == 0 ? 'vc-left-black' : '',
  194. isShowLeft && bgIndex == 1 ? 'vc-left-white' : '',
  195. curSentIndex == 0 ? 'hidden' : '',
  196. ]"
  197. @click="prevSentence"
  198. ></div>
  199. </div>
  200. <div class="vc-main">
  201. <div class="NNPE-words-box">
  202. <div
  203. class="NNPE-words"
  204. v-for="(pItem, pIndex) in item"
  205. :key="'wordsList' + pIndex"
  206. :class="[
  207. pItem.chs != '“' && pItem.wordIndex == 0
  208. ? 'textLeft'
  209. : 'textCenter',
  210. pItem.chs == '“' ? 'textRight' : '',
  211. ]"
  212. @dblclick="showWordDetail($event, pItem)"
  213. @click="playWord(pItem)"
  214. >
  215. <template v-if="!pItem.width">
  216. <template v-if="pItem.isShow">
  217. <template
  218. v-if="
  219. item[pIndex + 1] &&
  220. item[pIndex + 1].chs &&
  221. chsFhList.indexOf(item[pIndex + 1].chs) > -1
  222. "
  223. >
  224. <span class="NNPE-words-box">
  225. <template v-if="curQue.pyPosition == 'top'">
  226. <span
  227. v-if="config.isShowPY"
  228. class="NNPE-pinyin"
  229. :class="[
  230. pItem.className ? pItem.className : '',
  231. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  232. bgIndex == 1 ? 'font-white' : '',
  233. ]"
  234. :style="'font-size:' + pySize + 'px'"
  235. >{{ pItem.pinyin }}</span
  236. >
  237. </template>
  238. <span
  239. class="NNPE-chs"
  240. :class="[
  241. pItem.padding && config.isShowPY ? 'padding' : '',
  242. curQue.pyPosition == 'top' ? 'bottom' : '',
  243. ]"
  244. >
  245. <template>
  246. <span
  247. v-for="(wItem, wIndex) in pItem.leg"
  248. :key="'ci' + wIndex + pIndex"
  249. :class="[
  250. isPlaying &&
  251. pItem.timeList &&
  252. pItem.timeList[wIndex] &&
  253. curTime >= pItem.timeList[wIndex].wordBg &&
  254. curQue.wordTime &&
  255. curQue.wordTime[curSentIndex] &&
  256. curTime <= curQue.wordTime[curSentIndex].ed
  257. ? bgIndex == 0
  258. ? 'active'
  259. : 'active-yellow'
  260. : '',
  261. bgIndex == 1 ? 'font-white' : '',
  262. bgIndex == 0 && wordIndex == pItem.wordIndex
  263. ? 'wordActive'
  264. : '',
  265. bgIndex == 1 && wordIndex == pItem.wordIndex
  266. ? 'wordActive-blue'
  267. : '',
  268. ]"
  269. :style="'font-size:' + hzSize + 'px'"
  270. >{{ pItem.chs[wIndex] }}</span
  271. >
  272. </template>
  273. </span>
  274. <template v-if="curQue.pyPosition == 'bottom'">
  275. <span
  276. v-if="config.isShowPY"
  277. class="NNPE-pinyin bottom"
  278. :class="[
  279. pItem.className ? pItem.className : '',
  280. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  281. bgIndex == 1 ? 'font-white' : '',
  282. ]"
  283. :style="'font-size:' + pySize + 'px'"
  284. >{{ pItem.pinyin }}</span
  285. >
  286. </template>
  287. </span>
  288. <span class="NNPE-words-box">
  289. <template v-if="curQue.pyPosition == 'top'">
  290. <span
  291. v-if="config.isShowPY"
  292. :class="[
  293. 'NNPE-pinyin',
  294. noFont.indexOf(item[pIndex + 1].pinyin) > -1
  295. ? 'noFont'
  296. : '',
  297. bgIndex == 1 ? 'font-white' : '',
  298. ]"
  299. :style="{ fontSize: pySize + 'px', textAlign: left }"
  300. >{{ item[pIndex + 1].pinyin }}</span
  301. >
  302. </template>
  303. <span
  304. :class="[
  305. 'NNPE-chs',
  306. curQue.pyPosition == 'top' ? 'bottom' : '',
  307. ]"
  308. :style="{ fontSize: hzSize + 'px', textAlign: left }"
  309. >
  310. <span
  311. :class="[
  312. isPlaying &&
  313. pItem.timeList[pItem.leg - 1] &&
  314. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  315. curQue.wordTime &&
  316. curQue.wordTime[curSentIndex] &&
  317. curTime <= curQue.wordTime[curSentIndex].ed
  318. ? bgIndex == 0
  319. ? 'active'
  320. : 'active-yellow'
  321. : '',
  322. bgIndex == 1 ? 'font-white' : '',
  323. ]"
  324. :style="{ fontSize: pySize + 'px' }"
  325. >{{ item[pIndex + 1].chs }}</span
  326. >
  327. </span>
  328. <template v-if="curQue.pyPosition == 'bottom'">
  329. <span
  330. v-if="config.isShowPY"
  331. :class="[
  332. 'NNPE-pinyin',
  333. noFont.indexOf(item[pIndex + 1].pinyin) > -1
  334. ? 'noFont'
  335. : '',
  336. bgIndex == 1 ? 'font-white' : '',
  337. 'bottom',
  338. ]"
  339. :style="{ fontSize: pySize + 'px', textAlign: left }"
  340. >{{ item[pIndex + 1].pinyin }}</span
  341. >
  342. </template>
  343. </span>
  344. <span
  345. class="NNPE-words-box"
  346. v-if="
  347. item[pIndex + 2] &&
  348. item[pIndex + 2].chs &&
  349. chsFhList.indexOf(item[pIndex + 2].chs) > -1
  350. "
  351. >
  352. <template v-if="curQue.pyPosition == 'top'">
  353. <span
  354. v-if="config.isShowPY"
  355. :class="[
  356. 'NNPE-pinyin',
  357. noFont.indexOf(item[pIndex + 2].pinyin) > -1
  358. ? 'noFont'
  359. : '',
  360. bgIndex == 1 ? 'font-white' : '',
  361. ]"
  362. :style="{ fontSize: pySize + 'px', textAlign: left }"
  363. >{{ item[pIndex + 2].pinyin }}</span
  364. >
  365. </template>
  366. <span
  367. :class="[
  368. 'NNPE-chs',
  369. curQue.pyPosition == 'top' ? 'bottom' : '',
  370. ]"
  371. :style="{ fontSize: hzSize + 'px', textAlign: left }"
  372. >
  373. <span
  374. :class="[
  375. isPlaying &&
  376. pItem.timeList[pItem.leg - 1] &&
  377. curTime >= pItem.timeList[pItem.leg - 1].wordBg &&
  378. curQue.wordTime &&
  379. curQue.wordTime[curSentIndex] &&
  380. curTime <= curQue.wordTime[curSentIndex].ed
  381. ? bgIndex == 0
  382. ? 'active'
  383. : 'active-yellow'
  384. : '',
  385. bgIndex == 1 ? 'font-white' : '',
  386. ]"
  387. :style="{ fontSize: pySize + 'px' }"
  388. >{{ item[pIndex + 2].chs }}</span
  389. >
  390. </span>
  391. <template v-if="curQue.pyPosition == 'bottom'">
  392. <span
  393. v-if="config.isShowPY"
  394. :class="[
  395. 'NNPE-pinyin',
  396. noFont.indexOf(item[pIndex + 2].pinyin) > -1
  397. ? 'noFont'
  398. : '',
  399. bgIndex == 1 ? 'font-white' : '',
  400. 'bottom',
  401. ]"
  402. :style="{ fontSize: pySize + 'px', textAlign: left }"
  403. >{{ item[pIndex + 2].pinyin }}</span
  404. >
  405. </template>
  406. </span>
  407. </template>
  408. <template v-else>
  409. <template v-if="curQue.pyPosition == 'top'">
  410. <template v-if="NumberList.indexOf(pItem.pinyin) < 0">
  411. <span
  412. v-if="config.isShowPY"
  413. class="NNPE-pinyin"
  414. :class="[
  415. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  416. pItem.className ? pItem.className : '',
  417. noFont.indexOf(pItem.pinyin) > -1 ? 'noFont' : '',
  418. bgIndex == 1 ? 'font-white' : '',
  419. ]"
  420. :style="{ fontSize: pySize + 'px' }"
  421. >{{ pItem.pinyin }}</span
  422. >
  423. </template>
  424. </template>
  425. <span
  426. v-if="pItem.chs != '#'"
  427. class="NNPE-chs"
  428. :class="[
  429. pItem.chs != '“' && pItem.padding && config.isShowPY
  430. ? 'padding'
  431. : '',
  432. curQue.pyPosition == 'top' ? 'bottom' : '',
  433. ]"
  434. >
  435. <template>
  436. <span
  437. v-for="(wItem, wIndex) in pItem.leg"
  438. :key="'ci' + wIndex + pIndex + curSentIndex"
  439. :class="[
  440. isPlaying &&
  441. pItem.timeList &&
  442. pItem.timeList[wIndex] &&
  443. curTime >= pItem.timeList[wIndex].wordBg &&
  444. curQue.wordTime &&
  445. curQue.wordTime[curSentIndex] &&
  446. curTime <= curQue.wordTime[curSentIndex].ed
  447. ? bgIndex == 0
  448. ? 'active'
  449. : 'active-yellow'
  450. : '',
  451. bgIndex == 1 ? 'font-white' : '',
  452. bgIndex == 0 && wordIndex == pItem.wordIndex
  453. ? 'wordActive'
  454. : '',
  455. bgIndex == 1 && wordIndex == pItem.wordIndex
  456. ? 'wordActive-blue'
  457. : '',
  458. ]"
  459. :style="{ fontSize: hzSize + 'px' }"
  460. >{{ pItem.chs[wIndex] }}</span
  461. >
  462. </template>
  463. </span>
  464. <template v-if="curQue.pyPosition == 'bottom'">
  465. <template v-if="NumberList.indexOf(pItem.pinyin) < 0">
  466. <span
  467. v-if="config.isShowPY"
  468. class="NNPE-pinyin bottom"
  469. :class="[
  470. pItem.chs != '“' && pItem.padding ? 'padding' : '',
  471. pItem.className ? pItem.className : '',
  472. bgIndex == 1 ? 'font-white' : '',
  473. ]"
  474. :style="{ fontSize: pySize + 'px' }"
  475. >{{ pItem.pinyin }}</span
  476. >
  477. </template>
  478. </template>
  479. </template>
  480. </template>
  481. </template>
  482. <template v-else>
  483. <span
  484. :style="{
  485. height: pItem.height + 'px',
  486. width: pItem.width + 'px',
  487. }"
  488. ></span>
  489. </template>
  490. </div>
  491. </div>
  492. <div style="clear: both; overflow: hidden"></div>
  493. <div
  494. v-if="enwords && config.isShowEN"
  495. :class="['enwords', bgIndex == 1 ? 'enwords-green' : '']"
  496. :style="{ fontSize: enSize + 'px' }"
  497. >
  498. {{ enwords }}
  499. </div>
  500. </div>
  501. <div
  502. class="vc-box-right"
  503. @mousemove="showPrevNext(true, 'isShowRight')"
  504. @mouseleave="showPrevNext(false, 'isShowRight')"
  505. >
  506. <div
  507. :class="[
  508. 'vc-left vc-right-grey',
  509. isShowRight && bgIndex == 0 ? 'vc-right-black' : '',
  510. isShowRight && bgIndex == 1 ? 'vc-right-white' : '',
  511. curSentIndex == sentList.length - 1 ? 'hidden' : '',
  512. ]"
  513. @click="nextSentence"
  514. ></div>
  515. </div>
  516. </div>
  517. <!-- v-show="patternType == '录音模式'" -->
  518. <div class="waveform-wrapper" v-show="patternType == '录音模式'">
  519. <div class="big">
  520. <div
  521. :class="[playList.indexOf('yp') != -1 ? 'play_erji' : 'erji']"
  522. v-if="LYstatus != '未开始'"
  523. @click="selepaly('yp')"
  524. >
  525. <template v-if="playList.indexOf('yp') != -1">
  526. <img src="../../../assets/NPC/qp-erji-sele.png" alt="" />
  527. </template>
  528. <template v-else>
  529. <img src="../../../assets/NPC/qp-erji.png" alt="" />
  530. </template>
  531. </div>
  532. <div class="big_dv2">
  533. <div
  534. @mousewheel="mousewheelEvent"
  535. @mousemove="mouseoverEvent"
  536. id="waveform_big"
  537. ref="waveform_big"
  538. />
  539. <div id="timeline" ref="timeline"></div>
  540. </div>
  541. </div>
  542. <div class="big" id="ly_big" style="display: none">
  543. <div
  544. :class="[playList.indexOf('ly') != -1 ? 'play_erji' : 'erji']"
  545. @click="selepaly('ly')"
  546. >
  547. <!--
  548. v-if="LYstatus != '未开始'"
  549. -->
  550. <template v-if="playList.indexOf('ly') != -1">
  551. <img src="../../../assets/NPC/qp-erji-sele.png" alt="" />
  552. </template>
  553. <template v-else>
  554. <img src="../../../assets/NPC/qp-erji.png" alt="" />
  555. </template>
  556. </div>
  557. <div class="big_dv2">
  558. <div
  559. @mousewheel="LYmousewheelEvent"
  560. id="waveform_ly"
  561. ref="waveform_ly"
  562. class="elem"
  563. style="height: 130px"
  564. />
  565. <div id="timeline_ly" ref="timeline_ly"></div>
  566. </div>
  567. </div>
  568. </div>
  569. <div class="voicefull-bottom">
  570. <!--
  571. @mouseover="setBottomShow(true)"
  572. @mouseleave="setBottomShow(false)"
  573. -->
  574. <div
  575. :class="[
  576. isBottomShow ? 'voicefull-bottom-show' : 'voicefull-bottom-hidden',
  577. ]"
  578. >
  579. <div
  580. :class="[
  581. 'bottom-left',
  582. TaskModel == 'ANSWER' ? 'bottom-left-margin' : '',
  583. ]"
  584. >
  585. <!-- <Soundrecorddiff
  586. ref="Soundrecorddiff"
  587. @handleWav="handleWav"
  588. @getWavblob="getWavblob"
  589. @handleParentPlay="handleParentPlay"
  590. @sentPause="sentPause"
  591. @getRerordStatus="getRerordStatus"
  592. @getMicrophoneStatus="getMicrophoneStatus"
  593. @getPlayStatus="getPlayStatus"
  594. :bgIndex="bgIndex"
  595. :TaskModel="TaskModel"
  596. :answerRecordList="
  597. curQue.Bookanswer.practiceModel[curSentIndex] &&
  598. curQue.Bookanswer.practiceModel[curSentIndex].recordList
  599. "
  600. :tmIndex="curSentIndex"
  601. :key="'Soundrecorddiff' + curSentIndex"
  602. />
  603. <div
  604. :class="['compare-box', bgIndex == 1 ? 'compare-box-white' : '']"
  605. v-if="isShowCompare"
  606. >
  607. <Audio-compare
  608. :bgIndex="bgIndex"
  609. type="full"
  610. :themeColor="themeColor"
  611. :index="curSentIndex"
  612. :sentIndex="curSentIndex"
  613. :url="curQue.mp3_list[0].id"
  614. :bg="bg"
  615. :ed="ed"
  616. :wavblob="wavblob"
  617. :getCurTime="getCurCompareTime"
  618. :sentPause="sentPause"
  619. :isRecord="isRecord"
  620. :handleChangeStopAudio="handleChangeStopAudio"
  621. :getPlayStatus="getPlayStatus"
  622. :key="'mp3Compare' + curSentIndex"
  623. />
  624. </div> -->
  625. <template v-if="patternType == '录音模式'">
  626. <template v-if="LYstatus == '未开始' || LYstatus == '已结束'">
  627. <img
  628. @click="startLY"
  629. src="../../../assets/NPC/qp-ly-start.png"
  630. alt=""
  631. />
  632. </template>
  633. <template v-else-if="LYstatus == '录音中'">
  634. <img
  635. @click="stopLY"
  636. src="../../../assets/NPC/qp-ly-stop.png"
  637. alt=""
  638. style="margin-right: 20px"
  639. />
  640. <img
  641. @click="endLY"
  642. src="../../../assets/NPC/qp-ly-end.png"
  643. alt=""
  644. />
  645. </template>
  646. <template v-else-if="LYstatus == '暂停中'">
  647. <img
  648. @click="goonLY"
  649. src="../../../assets/NPC/qp-ly-jx.png"
  650. alt=""
  651. style="margin-right: 20px"
  652. />
  653. <img
  654. @click="endLY"
  655. src="../../../assets/NPC/qp-ly-end.png"
  656. alt=""
  657. />
  658. </template>
  659. <template v-else>
  660. <span @click="playLY"> 播放录音 </span>
  661. </template>
  662. </template>
  663. <template v-else>
  664. <div
  665. :class="['pattern', bgIndex == 1 ? 'darcColor_pattern' : '']"
  666. style="margin-right: 27px"
  667. >
  668. <template v-if="bgIndex == 0">
  669. <img src="../../../assets/NPC/biaozhu-pattern.png" alt="" />
  670. </template>
  671. <template v-else>
  672. <img
  673. src="../../../assets/NPC/darcColor-biaozhu-pattern.png"
  674. alt=""
  675. />
  676. </template>
  677. </div>
  678. <div
  679. :class="['pattern', bgIndex == 1 ? 'darcColor_pattern' : '']"
  680. @click="cutPatternType('录音模式')"
  681. >
  682. <template v-if="bgIndex == 0">
  683. <img src="../../../assets/NPC/luyin-pattern.png" alt="" />
  684. </template>
  685. <template v-else>
  686. <img
  687. src="../../../assets/NPC/darcColor-luyin-pattern.png"
  688. alt=""
  689. />
  690. </template>
  691. </div>
  692. </template>
  693. </div>
  694. <div v-if="patternType == '录音模式'">
  695. <div class="cuttentime">{{ ShowcurentTime }}</div>
  696. <div class="operate">
  697. <img
  698. v-if="LYstatus == '录音中'"
  699. src="../../../assets/NPC/qp-back-gray.png"
  700. alt=""
  701. style="width: 48px; height: 48px"
  702. />
  703. <img
  704. v-else
  705. src="../../../assets/NPC/qp-back.png"
  706. alt=""
  707. style="width: 48px; height: 48px"
  708. @click="backStatus(false)"
  709. />
  710. <div
  711. :class="[
  712. 'speed',
  713. speedListShow ? 'speed_sele' : '',
  714. LYstatus == '录音中' ? 'gray_speed' : '',
  715. ]"
  716. @click="speedListShowEvent"
  717. >
  718. <div v-if="speedListShow" :class="['speedList']">
  719. <div
  720. v-for="(item, i) in speedList"
  721. :key="i + 'speedList'"
  722. @click.stop="selespeed(i)"
  723. >
  724. {{ item }}×
  725. </div>
  726. </div>
  727. {{ speedList[speedIndex] }}×
  728. </div>
  729. <img
  730. v-if="LYstatus == '录音中'"
  731. src="../../../assets/NPC/play-fill-gray.png"
  732. alt=""
  733. style="width: 48px; height: 48px"
  734. />
  735. <template v-else>
  736. <img
  737. v-show="isPlaying"
  738. @click="playMusic('pause')"
  739. src="../../../assets/NPC/pause-fill.png"
  740. alt=""
  741. style="width: 16px; height: 24px"
  742. />
  743. <img
  744. v-show="!isPlaying"
  745. @click="playMusic('play')"
  746. src="../../../assets/NPC/play-fill.png"
  747. alt=""
  748. style="width: 21px; height: 24px"
  749. />
  750. </template>
  751. <img
  752. v-if="LYstatus == '录音中'"
  753. src="../../../assets/NPC/qp-xunhuan-gray.png"
  754. alt=""
  755. style="width: 48px; height: 48px"
  756. />
  757. <template v-else>
  758. <img
  759. src="../../../assets/NPC/qp-xunhuan.png"
  760. alt=""
  761. style="width: 48px; height: 48px"
  762. @click="circulationPlay"
  763. v-if="xunhunShow"
  764. />
  765. <img
  766. src="../../../assets/NPC/qp-no-xunhuan.png"
  767. alt=""
  768. style="width: 48px; height: 48px"
  769. @click="circulationPlay"
  770. v-else
  771. />
  772. </template>
  773. <img
  774. v-if="LYstatus != '已结束'"
  775. src="../../../assets/NPC/qp-duibi.png"
  776. alt=""
  777. style="width: 48px; height: 48px"
  778. />
  779. <img
  780. v-else
  781. src="../../../assets/NPC/qp-duibi-sele.png"
  782. alt=""
  783. style="width: 48px; height: 48px"
  784. @click="comparisonPlay"
  785. />
  786. </div>
  787. </div>
  788. <div
  789. :class="[
  790. 'page-count',
  791. bgIndex == 0 ? 'page-count-white' : 'page-count-green',
  792. ]"
  793. >
  794. {{ curSentIndex + 1 }}/{{ sentList.length }}
  795. <div v-if="patternType == '录音模式'">
  796. <img
  797. src="../../../assets/NPC/qp-last.png"
  798. alt=""
  799. style="width: 48px; height: 48px"
  800. @click="prevSentence"
  801. />
  802. <img
  803. src="../../../assets/NPC/qp-next.png"
  804. alt=""
  805. style="width: 48px; height: 48px; margin-left: 13px"
  806. @click="nextSentence"
  807. />
  808. </div>
  809. </div>
  810. </div>
  811. </div>
  812. <template v-if="isShow">
  813. <div
  814. ref="wordcard"
  815. class="NNPE-wordDetail"
  816. :style="{ top: top + 'px', left: left + 'px' }"
  817. >
  818. <Wordcard
  819. :word="word"
  820. :changeWordCard="changeWordCard"
  821. :themeColor="themeColor"
  822. :currentTreeID="currentTreeID"
  823. />
  824. </div>
  825. </template>
  826. <div class="word-play-audio" v-if="isWordPlay">
  827. <AudioLineSentence
  828. :mp3="mp3"
  829. :getCurTime="getCurWordTime"
  830. ref="audioLineWord"
  831. :audioId="'artPraAudioId' + curSentIndex + wordIndex"
  832. :stopAudio="stopAudio"
  833. :width="120"
  834. :hideSlider="false"
  835. :bg="wordbg"
  836. :ed="worded"
  837. :maxTime="wordMaxTime"
  838. :bgIndex="bgIndex"
  839. :isRepeat="isRepeat"
  840. :wordPlay="true"
  841. @changePlayStatus="changePlayStatus"
  842. />
  843. </div>
  844. </template>
  845. </div>
  846. </template>
  847. <script>
  848. import { Base64 } from "js-base64";
  849. function pcmtoWav(pcmsrt, sampleRate, numChannels, bitsPerSample) {
  850. return new Promise((resolve, reject) => {
  851. //参数->(base64编码的pcm流,采样频率,声道数,采样位数)
  852. let header = {
  853. // OFFS SIZE NOTES
  854. chunkId: [0x52, 0x49, 0x46, 0x46], // 0 4 "RIFF" = 0x52494646
  855. chunkSize: 0, // 4 4 36+SubChunk2Size = 4+(8+SubChunk1Size)+(8+SubChunk2Size)
  856. format: [0x57, 0x41, 0x56, 0x45], // 8 4 "WAVE" = 0x57415645
  857. subChunk1Id: [0x66, 0x6d, 0x74, 0x20], // 12 4 "fmt " = 0x666d7420
  858. subChunk1Size: 16, // 16 4 16 for PCM
  859. audioFormat: 1, // 20 2 PCM = 1
  860. numChannels: numChannels || 1, // 22 2 Mono = 1, Stereo = 2...
  861. sampleRate: sampleRate || 16000, // 24 4 8000, 44100...
  862. byteRate: 0, // 28 4 SampleRate*NumChannels*BitsPerSample/8
  863. blockAlign: 0, // 32 2 NumChannels*BitsPerSample/8
  864. bitsPerSample: bitsPerSample || 16, // 34 2 8 bits = 8, 16 bits = 16
  865. subChunk2Id: [0x64, 0x61, 0x74, 0x61], // 36 4 "data" = 0x64617461
  866. subChunk2Size: 0, // 40 4 data size = NumSamples*NumChannels*BitsPerSample/8
  867. };
  868. function u32ToArray(i) {
  869. return [i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, (i >> 24) & 0xff];
  870. }
  871. function u16ToArray(i) {
  872. return [i & 0xff, (i >> 8) & 0xff];
  873. }
  874. let pcm = Base64.toUint8Array(pcmsrt);
  875. header.blockAlign = (header.numChannels * header.bitsPerSample) >> 3;
  876. header.byteRate = header.blockAlign * header.sampleRate;
  877. header.subChunk2Size = pcm.length * (header.bitsPerSample >> 3);
  878. header.chunkSize = 36 + header.subChunk2Size;
  879. let wavHeader = header.chunkId.concat(
  880. u32ToArray(header.chunkSize),
  881. header.format,
  882. header.subChunk1Id,
  883. u32ToArray(header.subChunk1Size),
  884. u16ToArray(header.audioFormat),
  885. u16ToArray(header.numChannels),
  886. u32ToArray(header.sampleRate),
  887. u32ToArray(header.byteRate),
  888. u16ToArray(header.blockAlign),
  889. u16ToArray(header.bitsPerSample),
  890. header.subChunk2Id,
  891. u32ToArray(header.subChunk2Size)
  892. );
  893. let wavHeaderUnit8 = new Uint8Array(wavHeader);
  894. let mergedArray = new Uint8Array(wavHeaderUnit8.length + pcm.length);
  895. mergedArray.set(wavHeaderUnit8);
  896. mergedArray.set(pcm, wavHeaderUnit8.length);
  897. let blob = new Blob([mergedArray], { type: "audio/wav" });
  898. let blobUrl = window.URL.createObjectURL(blob);
  899. resolve(blobUrl);
  900. });
  901. }
  902. import AudioLineSentence from "./AudioLineSentence.vue";
  903. import Soundrecorddiff from "./Soundrecorddiff.vue";
  904. import AudioCompare from "./AudioCompare.vue";
  905. import Wordcard from "./components/Wordcard.vue";
  906. import { LearnWebSI, WebFileDownload } from "../../../api/ajax";
  907. // import Recorder from "js-audio-recorder"; // 录音插件
  908. import Recorder from "recorder-core";
  909. import "recorder-core/src/engine/mp3";
  910. import "recorder-core/src/engine/mp3-engine"; //如果此格式有额外的编码引擎(*-engine.js)的话,必须要加上
  911. import "recorder-core/src/extensions/waveview";
  912. import "recorder-core/src/extensions/wavesurfer.view.js";
  913. import "recorder-core/src/extensions/buffer_stream.player";
  914. import "recorder-core/src/extensions/lib.fft";
  915. import "recorder-core/src/extensions/frequency.histogram.view";
  916. import WaveSurfer from "wavesurfer.js";
  917. import Regions from "wavesurfer.js/dist/plugin/wavesurfer.regions.js";
  918. import CursorPlugin from "wavesurfer.js/dist/plugin/wavesurfer.cursor.js";
  919. import Timeline from "wavesurfer.js/dist/plugin/wavesurfer.timeline.js";
  920. var stream = Recorder.BufferStreamPlayer({
  921. play: true, //要播放声音,设为false不播放,只提供MediaStream
  922. realtime: true /*默认为true实时模式,设为false为非实时模式
  923. 实时模式:
  924. 如果有新的input输入数据,但之前输入的数据还未播放完,如果积压的数据量过大则积压的数据将会被直接丢弃,少量积压会和新数据一起加速播放,最终达到尽快播放新输入的数据的目的;这在网络不流畅卡顿时会发挥很大作用,可有效降低播放延迟
  925. 非实时模式:
  926. 连续完整的播放完所有input输入的数据,之前输入的还未播放完又有新input输入会加入队列排队播放,比如用于:一次性同时输入几段音频完整播放
  927. */,
  928. //,onInputError:fn(errMsg, inputIndex) //当input输入出错时回调,参数为input第几次调用和错误消息
  929. //,onUpdateTime:fn() //已播放时长、总时长更新回调(stop、pause、resume后一定会回调),this.currentTime为已播放时长,this.duration为已输入的全部数据总时长(实时模式下意义不大,会比实际播放的长),单位都是ms
  930. //,onPlayEnd:fn() //没有可播放的数据时回调(stop后一定会回调),已输入的数据已全部播放完了,可代表正在缓冲中或播放结束;之后如果继续input输入了新数据,播放完后会再次回调,因此会多次回调;非实时模式一次性输入了数据时,此回调相当于播放完成,可以stop掉,重新创建对象来input数据可达到循环播放效果
  931. //,decode:false //input输入的数据在调用transform之前是否要进行一次音频解码成pcm [Int16,...]
  932. //mp3、wav等都可以设为true,会自动解码成pcm
  933. //transform:fn(inputData,sampleRate,True,False)
  934. //将input输入的data(如果开启了decode将是解码后的pcm)转换处理成要播放的pcm数据;如果没有解码也没有提供本方法,input的data必须是[Int16,...]并且设置set.sampleRate
  935. //inputData:any input方法输入的任意格式数据,只要这个转换函数支持处理;如果开启了decode,此数据为input输入的数据解码后的pcm [Int16,...]
  936. //sampleRate:123 如果设置了decode为解码后的采样率,否则为set.sampleRate || null
  937. //True(pcm,sampleRate) 回调处理好的pcm数据([Int16,...])和pcm的采样率
  938. //False(errMsg) 处理失败回调
  939. //sampleRate:16000 //可选input输入的数据默认的采样率,当没有设置解码也没有提供transform时应当明确设置采样率
  940. });
  941. //创建好后第一件事就是start打开流,打开后就会开始播放input输入的音频
  942. stream.start(
  943. () => {
  944. // stream.currentTime;//当前已播放的时长,单位ms,数值变化时会有onUpdateTime事件
  945. // stream.duration;//已输入的全部数据总时长,单位ms,数值变化时会有onUpdateTime事件;实时模式下意义不大,会比实际播放的长,因为实时播放时卡了就会丢弃部分数据不播放
  946. // stream.isStop;//是否已停止,调用了stop方法时会设为true
  947. // stream.isPause;//是否已暂停,调用了pause方法时会设为true
  948. // stream.isPlayEnd;//已输入的数据是否播放到了结尾(没有可播放的数据了),input后又会变成false;可代表正在缓冲中或播放结束,状态变更时会有onPlayEnd事件
  949. // //如果不要默认的播放,可以设置set.play为false,这种情况下只拿到MediaStream来用
  950. // stream.getMediaStream() //通过getMediaStream方法得到MediaStream流,此流可以作为WebRTC的local流发送到对方,或者直接拿来赋值给audio.srcObject来播放(和赋值audio.src作用一致);未start时调用此方法将会抛异常
  951. // stream.getAudioSrc() //【已过时】超低版本浏览器中得到MediaStream流的字符串播放地址,可赋值给audio标签的src,直接播放音频;未start时调用此方法将会抛异常;新版本浏览器已停止支持将MediaStream转换成url字符串,调用本方法新浏览器会抛异常,因此在不需要兼容不支持srcObject的超低版本浏览器时,请直接使用getMediaStream然后赋值给auido.srcObject来播放
  952. },
  953. (errMsg) => {
  954. //start失败,无法播放
  955. }
  956. );
  957. var wavesurfer_ly = null;
  958. var wave;
  959. var lyData = {
  960. buffers: [],
  961. };
  962. export default {
  963. components: {
  964. AudioLineSentence,
  965. Soundrecorddiff,
  966. AudioCompare,
  967. Wordcard,
  968. },
  969. props: [
  970. "sentList",
  971. "sentIndex",
  972. "mp3",
  973. "wordTimeList",
  974. "curQue",
  975. "noFont",
  976. "themeColor",
  977. "NNPENewWordList",
  978. "currentTreeID",
  979. "config",
  980. "TaskModel",
  981. ],
  982. data() {
  983. return {
  984. speedListShow: false,
  985. speedList: ["2", "1.5", "1.25", "1", "0.75", "0.5"],
  986. speedIndex: 3,
  987. patternType: "",
  988. playList: [],
  989. // recorder: new Recorder({
  990. // sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
  991. // sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
  992. // numChannels: 1, // 声道,支持 1 或 2, 默认是1
  993. // compiling: true,
  994. // }),
  995. recorder: Recorder({
  996. //本配置参数请参考下面的文档,有详细介绍
  997. type: "mp3",
  998. sampleRate: 16000,
  999. bitRate: 16, //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
  1000. onProcess: function (
  1001. buffers,
  1002. powerLevel,
  1003. bufferDuration,
  1004. bufferSampleRate,
  1005. newBufferIdx,
  1006. asyncEnd
  1007. ) {
  1008. // let pcm = Recorder.SampleData(buffers);
  1009. // pcm.data.toString();
  1010. // let data = JSON.stringify(buffers[buffers.length - 1]);
  1011. // pcmtoWav(data).then((res) => {
  1012. // console.log(res);
  1013. // wavesurfer_ly.load(res);
  1014. // });
  1015. // const blob = new Blob(buffers[buffers.length - 1]);
  1016. // const objectUrl = window.URL.createObjectURL(blob);
  1017. // wavesurfer_ly.load(objectUrl);
  1018. // lyData.buffers = buffers;
  1019. // lyData.bufferSampleRate = bufferSampleRate;
  1020. wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate); //输入音频数据,更新显示波形
  1021. //录音实时回调,大约1秒调用12次本回调,buffers为开始到现在的所有录音pcm数据块(16位小端LE)
  1022. //可实时绘制波形(extensions目录内的waveview.js、wavesurfer.view.js、frequency.histogram.view.js插件功能)
  1023. //可利用extensions/sonic.js插件实时变速变调,此插件计算量巨大,onProcess需要返回true开启异步模式
  1024. //可实时上传(发送)数据,配合Recorder.SampleData方法,将buffers中的新数据连续的转换成pcm上传,或使用mock方法将新数据连续的转码成其他格式上传,可以参考文档里面的:Demo片段列表 -> 实时转码并上传-通用版;基于本功能可以做到:实时转发数据、实时保存数据、实时语音识别(ASR)等
  1025. },
  1026. }),
  1027. pySize: 32,
  1028. hzSize: 48,
  1029. enSize: 24,
  1030. bgIndex: 0,
  1031. maxTime: 0,
  1032. item: null,
  1033. bg: 0,
  1034. ed: 0,
  1035. isRepeat: false,
  1036. NumberList: [
  1037. "①",
  1038. "②",
  1039. "③",
  1040. "④",
  1041. "⑤",
  1042. "⑥",
  1043. "⑦",
  1044. "⑧",
  1045. "⑨",
  1046. "⑩",
  1047. "⑪",
  1048. "⑫",
  1049. "⑬",
  1050. "⑭",
  1051. "⑮",
  1052. "⑯",
  1053. "⑰",
  1054. "⑱",
  1055. "⑲",
  1056. "⑳",
  1057. ],
  1058. chsFhList: [",", "。", "”", ":", "》", "《", "?", "!", ";"],
  1059. enFhList: [",", ".", ";", "?", "!", ":", ">", "<"],
  1060. curTime: 0,
  1061. LY_curTime: 0,
  1062. wavblob: null,
  1063. stopAudio: false,
  1064. isRecord: false,
  1065. isShowCompare: false,
  1066. isShowRight: false,
  1067. isShowLeft: false,
  1068. curSentIndex: 0,
  1069. oldHz: "",
  1070. hz: "",
  1071. clientY: 0,
  1072. top: 0,
  1073. left: 0,
  1074. newWordList: [],
  1075. pinyin: "",
  1076. wordIndex: -1,
  1077. isShow: false,
  1078. wordbg: 0,
  1079. worded: 0,
  1080. wordMaxTime: 0,
  1081. isWordPlay: false,
  1082. curWordTime: 0,
  1083. isPlaying: false,
  1084. isAuto: false,
  1085. key: "isRepeat",
  1086. isKeyboard: true,
  1087. isTopShow: true,
  1088. isBottomShow: true,
  1089. isRecording: false,
  1090. recordPlaying: false,
  1091. isCollArr: [],
  1092. enwords: "",
  1093. screenHeight: 0,
  1094. wavesurfer: null,
  1095. wavesurfer_big: null,
  1096. loading: null,
  1097. timer: null,
  1098. ShowcurentTime: 0,
  1099. LYstatus: "未开始",
  1100. startLyShow: false,
  1101. //波浪图-录音
  1102. drawRecordId: null,
  1103. oCanvas: null,
  1104. ctx: null,
  1105. //波浪图-播放
  1106. drawPlayId: null,
  1107. pCanvas: null,
  1108. pCtx: null,
  1109. zoomNumber: 0,
  1110. lyzoomNumber: 1500,
  1111. xunhunShow: false,
  1112. regionData: null,
  1113. LY_regionData: null,
  1114. LY_url: "",
  1115. comparisonPlayStatus: false,
  1116. };
  1117. },
  1118. computed: {
  1119. // isPlaying: function () {
  1120. // let playing = false;
  1121. // if (this.$refs.audioLineSent) {
  1122. // playing = this.$refs.audioLineSent.audio.isPlaying;
  1123. // }
  1124. // console.log(playing);
  1125. // return playing;
  1126. // },
  1127. },
  1128. watch: {
  1129. isRecording: {
  1130. handler: function (newVal, oldVal) {
  1131. if (newVal) {
  1132. this.isBottomShow = newVal;
  1133. }
  1134. },
  1135. deep: true,
  1136. },
  1137. recordPlaying: {
  1138. handler: function (newVal, oldVal) {
  1139. if (newVal) {
  1140. this.isBottomShow = newVal;
  1141. }
  1142. },
  1143. deep: true,
  1144. },
  1145. sentIndex: {
  1146. handler: function (newVal, oldVal) {
  1147. this.curSentIndex = newVal;
  1148. this.getSentence();
  1149. },
  1150. deep: true,
  1151. },
  1152. hz: {
  1153. handler: function (val, oldVal) {
  1154. let _this = this;
  1155. if (val) {
  1156. _this.handleNewWords(val);
  1157. }
  1158. },
  1159. // 深度观察监听
  1160. deep: true,
  1161. },
  1162. isShow: {
  1163. handler: function (val, oldVal) {
  1164. let _this = this;
  1165. if (val) {
  1166. setTimeout(() => {
  1167. _this.cardHeight = _this.$refs.wordcard.offsetHeight;
  1168. if (_this.screenHeight - _this.clientY > _this.cardHeight) {
  1169. _this.top = _this.clientY + 20;
  1170. } else {
  1171. _this.top = _this.clientY - _this.cardHeight - 30;
  1172. }
  1173. }, 50);
  1174. }
  1175. },
  1176. // 深度观察监听
  1177. deep: true,
  1178. },
  1179. },
  1180. //方法集合
  1181. methods: {
  1182. // 对比播放
  1183. comparisonPlay() {
  1184. this.stopAllPlayStart();
  1185. this.comparisonPlayStatus = true;
  1186. this.curTime = 0;
  1187. this.playList = [];
  1188. this.playMusic("play");
  1189. },
  1190. // 暂停并回到起点
  1191. stopAllPlayStart() {
  1192. if (this.wavesurfer_big) {
  1193. this.wavesurfer_big.stop();
  1194. }
  1195. if (this.LYstatus == "已结束") {
  1196. wavesurfer_ly.stop();
  1197. }
  1198. },
  1199. // 循环播放
  1200. circulationPlay() {
  1201. this.xunhunShow = !this.xunhunShow;
  1202. if (this.isPlaying) {
  1203. this.stopPlay();
  1204. this.playMusic("play");
  1205. }
  1206. },
  1207. // 选择速度
  1208. speedListShowEvent() {
  1209. if (this.LYstatus == "录音中") {
  1210. return;
  1211. }
  1212. this.speedListShow = !this.speedListShow;
  1213. },
  1214. selespeed(index) {
  1215. let ly_Playing = false;
  1216. let yp_Playing = false;
  1217. if (this.isPlaying) {
  1218. if (this.playList != ["ly"]) {
  1219. this.curTime = this.wavesurfer_big.getCurrentTime();
  1220. yp_Playing = true;
  1221. } else {
  1222. ly_Playing = true;
  1223. }
  1224. }
  1225. this.speedListShow = false;
  1226. this.speedIndex = index;
  1227. this.initaudioImage(this.speedList[index], yp_Playing);
  1228. if (this.LYstatus == "已结束") {
  1229. this.initLYaudioImage(this.speedList[this.sentIndex], ly_Playing);
  1230. }
  1231. },
  1232. // 返回初始状态
  1233. backStatus(bool) {
  1234. this.stopAllPlayStart();
  1235. this.regionData = null;
  1236. this.LY_regionData = null;
  1237. this.speedIndex = 3;
  1238. this.ShowcurentTime = 0;
  1239. this.curTime = 0;
  1240. this.LY_curTime = 0;
  1241. this.xunhunShow = false;
  1242. this.wavesurfer_big = null;
  1243. this.initaudioImage(1, bool ? true : false);
  1244. if (this.LYstatus == "已结束") {
  1245. wavesurfer_ly = null;
  1246. this.initLYaudioImage(1, false);
  1247. }
  1248. },
  1249. changetime() {
  1250. let _this = this;
  1251. if (_this.curSentIndex != 0) {
  1252. _this.item.forEach((items) => {
  1253. items.timeList.forEach((it) => {
  1254. let AllNullTime = 0;
  1255. for (let i = _this.curSentIndex; i > 0; i--) {
  1256. if (i > 0) {
  1257. AllNullTime +=
  1258. _this.curQue.wordTime[i].bg - _this.curQue.wordTime[i - 1].ed;
  1259. }
  1260. }
  1261. it.wordBg =
  1262. it.wordBg - _this.curQue.wordTime[_this.curSentIndex].bg;
  1263. it.wordEd =
  1264. it.wordEd - _this.curQue.wordTime[_this.curSentIndex].bg;
  1265. });
  1266. });
  1267. let AllNullTime = 0;
  1268. for (let i = _this.curSentIndex; i > 0; i--) {
  1269. if (i > 0) {
  1270. AllNullTime +=
  1271. _this.curQue.wordTime[i].bg - _this.curQue.wordTime[i - 1].ed;
  1272. }
  1273. }
  1274. _this.ed =
  1275. _this.curQue.wordTime[_this.curSentIndex].ed -
  1276. _this.curQue.wordTime[_this.curSentIndex].bg;
  1277. _this.bg =
  1278. _this.curQue.wordTime[_this.curSentIndex].bg -
  1279. _this.curQue.wordTime[_this.curSentIndex].bg;
  1280. }
  1281. },
  1282. // 切换录音模式
  1283. cutPatternType(type) {
  1284. let _this = this;
  1285. _this.$nextTick(() => {
  1286. if (_this.$refs.audioLineSent) {
  1287. _this.$refs.audioLineSent.PlayAudio();
  1288. }
  1289. });
  1290. this.patternType = type;
  1291. if (type == "录音模式") {
  1292. this.changetime();
  1293. this.initaudioImage(null, false);
  1294. if (this.curQue.Bookanswer.practiceModel[this.curSentIndex]) {
  1295. this.curjuzi(
  1296. this.curQue.Bookanswer.practiceModel[this.curSentIndex].recordSrc,
  1297. false
  1298. );
  1299. this.LYstatus = "已结束";
  1300. } else {
  1301. this.curjuzi(null, false);
  1302. this.LYstatus = "未开始";
  1303. }
  1304. }
  1305. },
  1306. selepaly(type) {
  1307. if (this.LYstatus == "未开始") {
  1308. return;
  1309. }
  1310. let index = this.playList.indexOf(type);
  1311. if (index != -1) {
  1312. this.playList.splice(index, 1);
  1313. } else {
  1314. this.playList.push(type);
  1315. }
  1316. this.playMusic(this.isPlaying ? "puse" : "play");
  1317. },
  1318. startCanvas() {
  1319. //录音波浪
  1320. this.oCanvas = document.getElementById("canvas");
  1321. this.ctx = this.oCanvas.getContext("2d");
  1322. //播放波浪
  1323. this.pCanvas = document.getElementById("playChart");
  1324. this.pCtx = this.pCanvas.getContext("2d");
  1325. },
  1326. mouseoverEvent() {
  1327. let arr = this.wavesurfer_big.mediaContainer.textContent.split(":");
  1328. let number = "";
  1329. arr.forEach((item) => {
  1330. number += item;
  1331. });
  1332. number = (number * 1) / 1000;
  1333. this.zoomTime = number;
  1334. },
  1335. // 鼠标滚动放大缩小
  1336. mousewheelEvent(e) {
  1337. let that = this;
  1338. var ev = e || window.event;
  1339. var down = true;
  1340. down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
  1341. if (down) {
  1342. if (that.zoomNumber <= 0) {
  1343. return;
  1344. }
  1345. that.zoomNumber = that.zoomNumber - 100;
  1346. } else {
  1347. that.zoomNumber = that.zoomNumber + 100;
  1348. }
  1349. that.wavesurfer_big.zoom(Number(that.zoomNumber), this.zoomTime);
  1350. },
  1351. // 录音鼠标滚动放大缩小
  1352. LYmousewheelEvent(e) {
  1353. let that = this;
  1354. var ev = e || window.event;
  1355. var down = true;
  1356. down = ev.wheelDelta ? ev.wheelDelta < 0 : ev.detail > 0;
  1357. if (down) {
  1358. if (that.lyzoomNumber <= 0) {
  1359. return;
  1360. }
  1361. that.lyzoomNumber = that.lyzoomNumber - 100;
  1362. } else {
  1363. that.lyzoomNumber = that.lyzoomNumber + 100;
  1364. }
  1365. that.wavesurfer_ly.zoom(Number(that.lyzoomNumber), this.zoomTime);
  1366. },
  1367. // 初始化声波
  1368. initaudioImage(audioRate, isPlaying) {
  1369. this.loading = true;
  1370. let node = document.getElementById("waveform_big");
  1371. var pObjs = node.childNodes;
  1372. for (var i = pObjs.length - 1; i >= 0; i--) {
  1373. // 一定要倒序,正序是删不干净的,可自行尝试
  1374. node.removeChild(pObjs[i]);
  1375. }
  1376. let plugin = [
  1377. Timeline.create({
  1378. container: "#timeline",
  1379. primaryColor: "#c0c0c0",
  1380. secondaryColor: "#c0c0c0",
  1381. primaryFontColor: "#c0c0c0",
  1382. secondaryFontColor: "#c0c0c0",
  1383. formatTimeCallback: this.formatTimeCallback,
  1384. timeInterval: 0.025,
  1385. primaryLabelInterval: 4,
  1386. secondaryLabelInterval: 400,
  1387. notchPercentHeight: 40,
  1388. }),
  1389. CursorPlugin.create({
  1390. showTime: true,
  1391. opacity: 1,
  1392. color: "#1370F6",
  1393. customShowTimeStyle: {
  1394. "background-color": "#000",
  1395. color: "#fff",
  1396. "font-size": "10px",
  1397. },
  1398. }),
  1399. Regions.create({}),
  1400. ];
  1401. if (this.regionData) {
  1402. plugin.splice(plugin.length - 1, 1);
  1403. plugin.push(
  1404. Regions.create({
  1405. regions: [
  1406. {
  1407. start: this.regionData.start,
  1408. end: this.regionData.end,
  1409. loop: false,
  1410. drag: false,
  1411. resize: false,
  1412. color: "rgba(0, 180, 0, 0.5)",
  1413. },
  1414. ],
  1415. })
  1416. );
  1417. }
  1418. this.wavesurfer_big = WaveSurfer.create({
  1419. container: this.$refs.waveform_big,
  1420. audioRate: audioRate ? audioRate : 1,
  1421. barMinHeight: 0.5,
  1422. barWidth: 1,
  1423. backgroundColor: "#141414",
  1424. progressColor: "#44BB6C",
  1425. backend: "MediaElement",
  1426. waveColor: "#44BB6C",
  1427. cursorColor: "#1370F6",
  1428. cursorWidth: 3,
  1429. barHeight: 2,
  1430. barGap: 0,
  1431. height: this.LYstatus == "已结束" ? 130 : 308,
  1432. width: 400,
  1433. interact: true,
  1434. plugins: plugin,
  1435. });
  1436. if (!this.regionData) {
  1437. this.wavesurfer_big.addRegion({
  1438. loop: false,
  1439. drag: false,
  1440. resize: false,
  1441. // color: "rgba(254, 255, 255, 0.4)",
  1442. });
  1443. }
  1444. let that = this;
  1445. WebFileDownload({
  1446. FileID: this.curQue.time_space_list[this.curSentIndex].file_id,
  1447. }).then((res) => {
  1448. const objectUrl = window.URL.createObjectURL(res);
  1449. this.wavesurfer_big.load(objectUrl);
  1450. let start = this.bg / 1000;
  1451. let end = this.ed / 1000;
  1452. that.wavesurfer_big.on("ready", function (e) {
  1453. if (!that.regionData) {
  1454. that.wavesurfer_big.enableDragSelection({
  1455. color: "rgba(0, 180, 0, 0.5)",
  1456. });
  1457. that.wavesurfer_big.clearRegions(); // 音频加载完成
  1458. }
  1459. if (!audioRate) {
  1460. that.wavesurfer_big.play(start, end);
  1461. } else {
  1462. if (isPlaying) {
  1463. that.playMusic("play");
  1464. }
  1465. }
  1466. that.wavesurfer_big.zoom(0);
  1467. that.loading = false;
  1468. that.$forceUpdate();
  1469. });
  1470. });
  1471. // 更新区域时。回调将接收该Region对象。
  1472. that.wavesurfer_big.on("region-updated", function (region) {
  1473. // region.playLoop(); // 循环播放选中区域
  1474. // region.play()
  1475. that.regionData = region;
  1476. });
  1477. that.wavesurfer_big.on("region-created", () => {
  1478. that.wavesurfer_big.clearRegions();
  1479. });
  1480. that.wavesurfer_big.on("play", function (e) {
  1481. that.isPlaying = true;
  1482. });
  1483. that.wavesurfer_big.on("pause", function (e) {
  1484. that.isPlaying = false;
  1485. that.playList = [];
  1486. let time = that.wavesurfer_big.getCurrentTime();
  1487. if (that.xunhunShow) {
  1488. if (!that.regionData) {
  1489. if (time * 1000 == that.ed) {
  1490. if (that.comparisonPlayStatus) {
  1491. that.playList = ["ly"];
  1492. that.playMusic("play");
  1493. } else {
  1494. that.playMusic("play");
  1495. }
  1496. }
  1497. } else {
  1498. if (time.toFixed(2) == that.regionData.end.toFixed(2)) {
  1499. if (that.comparisonPlayStatus) {
  1500. that.playList = ["ly"];
  1501. that.playMusic("play");
  1502. } else {
  1503. that.playMusic("play");
  1504. }
  1505. }
  1506. }
  1507. } else {
  1508. if (that.comparisonPlayStatus) {
  1509. if (!that.regionData) {
  1510. if (time * 1000 == that.ed) {
  1511. if (that.comparisonPlayStatus) {
  1512. that.playList = ["ly"];
  1513. that.playMusic("play");
  1514. }
  1515. }
  1516. } else {
  1517. if (time.toFixed(2) == that.regionData.end.toFixed(2)) {
  1518. if (that.comparisonPlayStatus) {
  1519. that.playList = ["ly"];
  1520. that.playMusic("play");
  1521. }
  1522. }
  1523. }
  1524. }
  1525. }
  1526. });
  1527. that.wavesurfer_big.on("interaction", function (e) {
  1528. that.isPlaying = false;
  1529. that.stopPlay();
  1530. });
  1531. that.wavesurfer_big.on("audioprocess", function (e) {
  1532. that.curTime = e * 1000;
  1533. let time = e.toFixed(2);
  1534. that.playList = ["yp"];
  1535. let arr = time.split(".");
  1536. let newtime = "";
  1537. arr.forEach((item, i) => {
  1538. if (item.length == 1) {
  1539. item = "0" + item;
  1540. }
  1541. if (i == 0) {
  1542. newtime += item + ":";
  1543. } else {
  1544. newtime += item;
  1545. }
  1546. });
  1547. that.ShowcurentTime = newtime;
  1548. });
  1549. },
  1550. curjuzi(src) {
  1551. let _this = this;
  1552. let node = document.getElementById("waveform_big");
  1553. node.children[0].style.height = "130px";
  1554. let lynode = document.getElementById("ly_big");
  1555. lynode.style.display = "flex";
  1556. if (src) {
  1557. _this.LY_url = src;
  1558. } else {
  1559. let node = document.getElementById("waveform_big");
  1560. node.children[0].style.height = "308px";
  1561. let lynode = document.getElementById("ly_big");
  1562. lynode.style.display = "none";
  1563. }
  1564. },
  1565. // 初始化录音声波图
  1566. initLYaudioImage(audioRate) {
  1567. this.loading = true;
  1568. let node = document.getElementById("waveform_ly");
  1569. var pObjs = node.childNodes;
  1570. for (var i = pObjs.length - 1; i >= 0; i--) {
  1571. // 一定要倒序,正序是删不干净的,可自行尝试
  1572. node.removeChild(pObjs[i]);
  1573. }
  1574. let _this = this;
  1575. let plugin = [
  1576. Timeline.create({
  1577. container: "#timeline_ly",
  1578. primaryColor: "#c0c0c0",
  1579. secondaryColor: "#c0c0c0",
  1580. primaryFontColor: "#c0c0c0",
  1581. secondaryFontColor: "#c0c0c0",
  1582. formatTimeCallback: _this.formatTimeCallback,
  1583. timeInterval: 0.025,
  1584. primaryLabelInterval: 4,
  1585. secondaryLabelInterval: 400,
  1586. notchPercentHeight: 40,
  1587. }),
  1588. CursorPlugin.create({
  1589. showTime: true,
  1590. opacity: 1,
  1591. color: "#1370F6",
  1592. customShowTimeStyle: {
  1593. "background-color": "#000",
  1594. color: "#fff",
  1595. "font-size": "10px",
  1596. },
  1597. }),
  1598. Regions.create({}),
  1599. ];
  1600. if (this.LY_regionData) {
  1601. plugin.splice(plugin.length - 1, 1);
  1602. plugin.push(
  1603. Regions.create({
  1604. regions: [
  1605. {
  1606. start: this.LY_regionData.start,
  1607. end: this.LY_regionData.end,
  1608. loop: false,
  1609. drag: false,
  1610. resize: false,
  1611. color: "rgba(0, 180, 0, 0.3)",
  1612. },
  1613. ],
  1614. })
  1615. );
  1616. }
  1617. wavesurfer_ly = WaveSurfer.create({
  1618. container: _this.$refs.waveform_ly,
  1619. audioRate: audioRate ? audioRate : 1,
  1620. barMinHeight: 0.5,
  1621. barWidth: 1,
  1622. backgroundColor: "#141414",
  1623. progressColor: "#5370BB",
  1624. backend: "MediaElement",
  1625. waveColor: "#5370BB",
  1626. cursorColor: " #DE4444",
  1627. cursorWidth: 3,
  1628. barHeight: 2,
  1629. barGap: 0,
  1630. height: 130,
  1631. width: 400,
  1632. interact: true,
  1633. plugins: plugin,
  1634. });
  1635. if (!this.LY_regionData) {
  1636. wavesurfer_ly.addRegion({
  1637. loop: false,
  1638. drag: false,
  1639. resize: false,
  1640. color: "rgba(254, 255, 255, 0.4)",
  1641. });
  1642. }
  1643. wavesurfer_ly.load(_this.LY_url);
  1644. wavesurfer_ly.on("ready", function (e) {
  1645. wavesurfer_ly.zoom(Number(1500));
  1646. if (!_this.LY_regionData) {
  1647. wavesurfer_ly.enableDragSelection({
  1648. color: "rgba(0, 180, 0, 0.3)",
  1649. });
  1650. wavesurfer_ly.clearRegions(); // 音频加载完成
  1651. }
  1652. _this.loading = false;
  1653. });
  1654. wavesurfer_ly.on("play", function (e) {
  1655. _this.isPlaying = true;
  1656. });
  1657. wavesurfer_ly.on("interaction", function (e) {
  1658. _this.isPlaying = false;
  1659. wavesurfer_ly.pause();
  1660. });
  1661. wavesurfer_ly.on("pause", function (e) {
  1662. _this.isPlaying = false;
  1663. _this.playList = [];
  1664. let time = wavesurfer_ly.getCurrentTime();
  1665. if (_this.xunhunShow) {
  1666. if (time.toFixed(2) == _this.LY_regionData.end.toFixed(2)) {
  1667. if (_this.comparisonPlayStatus) {
  1668. _this.playList = [];
  1669. _this.playMusic("play");
  1670. } else {
  1671. _this.playList = ["ly"];
  1672. _this.playMusic("play");
  1673. }
  1674. }
  1675. } else {
  1676. if (time.toFixed(2) == _this.LY_regionData.end.toFixed(2)) {
  1677. _this.comparisonPlayStatus = false;
  1678. }
  1679. }
  1680. });
  1681. wavesurfer_ly.on("audioprocess", function (e) {
  1682. _this.playList = ["ly"];
  1683. _this.LY_curTime = e * 1000;
  1684. });
  1685. wavesurfer_ly.on("finish", function (e) {
  1686. if (_this.xunhunShow) {
  1687. if (_this.comparisonPlayStatus) {
  1688. _this.playList = ["yp"];
  1689. _this.playMusic("play");
  1690. } else {
  1691. _this.comparisonPlayStatus = false;
  1692. _this.playList = ["ly"];
  1693. _this.playMusic("play");
  1694. }
  1695. }
  1696. });
  1697. // 更新区域时。回调将接收该Region对象。
  1698. wavesurfer_ly.on("region-updated", function (region) {
  1699. // region.playLoop(); // 循环播放选中区域
  1700. // region.play()
  1701. _this.LY_regionData = region;
  1702. });
  1703. wavesurfer_ly.on("region-created", () => {
  1704. wavesurfer_ly.clearRegions();
  1705. });
  1706. },
  1707. formatTimeCallback(seconds, pxPerSec) {
  1708. seconds = Number(seconds);
  1709. let minutes = Math.floor(seconds / 60);
  1710. seconds = seconds % 60;
  1711. // fill up seconds with zeroes
  1712. let secondsStr = Math.round(seconds).toString();
  1713. if (pxPerSec >= 25 * 10) {
  1714. secondsStr = seconds.toFixed(2);
  1715. } else if (pxPerSec >= 25 * 1) {
  1716. secondsStr = seconds.toFixed(1);
  1717. }
  1718. if (minutes > 0) {
  1719. if (seconds < 10) {
  1720. secondsStr = "0" + secondsStr;
  1721. }
  1722. return `${minutes}:${secondsStr}`;
  1723. }
  1724. return secondsStr;
  1725. },
  1726. // 播放录音
  1727. playLY() {
  1728. if (this.LY_regionData) {
  1729. if (this.xunhunShow) {
  1730. // this.LY_regionData.loop = true;
  1731. // this.LY_regionData.playLoop(); // 循环播放选中区域
  1732. wavesurfer_ly.play(this.LY_regionData.start, this.LY_regionData.end);
  1733. } else {
  1734. // this.LY_regionData.loop = false;
  1735. wavesurfer_ly.play(this.LY_regionData.start, this.LY_regionData.end);
  1736. // this.LY_regionData.play();
  1737. }
  1738. } else {
  1739. wavesurfer_ly.play();
  1740. }
  1741. },
  1742. // 暂停播放
  1743. stopPlay() {
  1744. this.wavesurfer_big.pause();
  1745. if (this.LYstatus == "已结束") {
  1746. wavesurfer_ly.pause();
  1747. }
  1748. },
  1749. // 开始录音
  1750. startLY() {
  1751. this.stopPlay();
  1752. let node = document.getElementById("waveform_big");
  1753. node.children[0].style.height = "130px";
  1754. let lynode = document.getElementById("ly_big");
  1755. lynode.style.display = "flex";
  1756. // let lynodedv = document.getElementById("waveform_ly");
  1757. // lynodedv.children[0].style.height = "130px";
  1758. let _this = this;
  1759. // _this.recorder.start(); //此处可以立即开始录音,但不建议这样编写,因为open是一个延迟漫长的操作,通过两次用户操作来分别调用open和start是推荐的最佳流程
  1760. // _this.LYstatus = "录音中";
  1761. _this.recorder.open(
  1762. function () {
  1763. wave = Recorder.WaveSurferView({
  1764. elem: ".elem",
  1765. keep: true,
  1766. lineCount: 70,
  1767. position: 0,
  1768. minHeight: 1,
  1769. stripeEnable: false,
  1770. linear: [0, "#5370BB", 1, "#5370BB"],
  1771. // linear: [
  1772. // 0,
  1773. // "rgba(0,0,0,1)",
  1774. // 0.7,
  1775. // "rgba(0,0,0,1)",
  1776. // 1,
  1777. // "rgba(0,0,0,1)",
  1778. // ],
  1779. centerColor: "",
  1780. });
  1781. //打开麦克风授权获得相关资源
  1782. //dialog&&dialog.Cancel(); 如果开启了弹框,此处需要取消
  1783. _this.recorder.start(); //此处可以立即开始录音,但不建议这样编写,因为open是一个延迟漫长的操作,通过两次用户操作来分别调用open和start是推荐的最佳流程
  1784. _this.LYstatus = "录音中";
  1785. },
  1786. function (msg, isUserNotAllow) {
  1787. //用户拒绝未授权或不支持
  1788. //dialog&&dialog.Cancel(); 如果开启了弹框,此处需要取消
  1789. }
  1790. );
  1791. },
  1792. // 暂停录音
  1793. stopLY() {
  1794. this.recorder.pause();
  1795. this.LYstatus = "暂停中";
  1796. // this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
  1797. // this.drawRecordId = null;
  1798. },
  1799. // 继续录音
  1800. goonLY() {
  1801. this.stopPlay();
  1802. this.recorder.resume();
  1803. this.LYstatus = "录音中";
  1804. },
  1805. // 初始化录音声波
  1806. // 结束录音
  1807. endLY() {
  1808. let _this = this;
  1809. this.recorder.stop(
  1810. function (blob, duration) {
  1811. console.log(
  1812. blob,
  1813. (window.URL || webkitURL).createObjectURL(blob),
  1814. "时长:" + duration + "ms"
  1815. );
  1816. _this.recorder.close(); //释放录音资源,当然可以不释放,后面可以连续调用start;但不释放时系统或浏览器会一直提示在录音,最佳操作是录完就close掉
  1817. /*** 【立即播放例子】 ***/
  1818. let lynodedv = document.getElementById("waveform_ly");
  1819. lynodedv.removeChild(lynodedv.children[0]);
  1820. const objectUrl = window.URL.createObjectURL(blob);
  1821. _this.LY_url = objectUrl;
  1822. _this.initLYaudioImage();
  1823. if (_this.curQue.Bookanswer.practiceModel[_this.curSentIndex]) {
  1824. _this.curQue.Bookanswer.practiceModel[_this.curSentIndex] = {
  1825. recordSrc: objectUrl,
  1826. };
  1827. } else {
  1828. _this.curQue.Bookanswer.practiceModel[_this.curSentIndex] = {
  1829. recordSrc: objectUrl,
  1830. };
  1831. }
  1832. },
  1833. function (msg) {
  1834. console.log("录音失败:" + msg);
  1835. _this.recorder.close(); //可以通过stop方法的第3个参数来自动调用close
  1836. }
  1837. );
  1838. this.LYstatus = "已结束";
  1839. // this.drawRecordId && cancelAnimationFrame(this.drawRecordId);
  1840. // this.drawRecordId = null;
  1841. },
  1842. setTopShow(bool) {
  1843. this.isTopShow = bool;
  1844. },
  1845. setBottomShow(bool) {
  1846. if (!this.recordPlaying && !this.isRecording) {
  1847. this.isBottomShow = bool;
  1848. }
  1849. },
  1850. getPlayStatus(bool) {
  1851. this.recordPlaying = bool;
  1852. },
  1853. setFontSize(type) {
  1854. let _this = this;
  1855. if (type == "-") {
  1856. if (_this.hzSize >= 34) {
  1857. this.hzSize = this.hzSize - 4;
  1858. }
  1859. }
  1860. if (type == "+") {
  1861. if (_this.hzSize <= 76) {
  1862. this.hzSize = this.hzSize + 4;
  1863. }
  1864. }
  1865. _this.pySize = parseInt(_this.hzSize / 1.5);
  1866. _this.enSize = parseInt(_this.hzSize / 2);
  1867. },
  1868. playChange(bool) {
  1869. this.isPlaying = bool;
  1870. },
  1871. handleColl() {
  1872. let _this = this;
  1873. if (_this.isCollArr[_this.curSentIndex]) {
  1874. _this.cancleColl();
  1875. } else {
  1876. _this.addColl();
  1877. }
  1878. },
  1879. //添加收藏
  1880. addColl() {
  1881. let Bookdetail = sessionStorage.getItem("Bookdetail");
  1882. if (Bookdetail) {
  1883. Bookdetail = JSON.parse(Bookdetail);
  1884. let MethodName = "order-collection_manager-AddMyCollection";
  1885. let text = "";
  1886. this.item.forEach((item) => {
  1887. if (item.chs != "#") {
  1888. text += item.chs;
  1889. }
  1890. });
  1891. let sentence_json = {
  1892. item: JSON.stringify(this.item),
  1893. bg: this.bg,
  1894. ed: this.ed,
  1895. mp3: this.mp3,
  1896. pyPosition: this.curQue.pyPosition,
  1897. };
  1898. let data = {
  1899. goods_id: this.currentTreeID,
  1900. goods_type: 502,
  1901. goods_name: Bookdetail.name,
  1902. goods_person_name_desc: Bookdetail.description
  1903. ? Bookdetail.description
  1904. : "",
  1905. goods_picture_id: Bookdetail.picture_id ? Bookdetail.picture_id : "",
  1906. goods_price: Bookdetail.price,
  1907. sentence: {
  1908. sentence_text: text,
  1909. sentence_json: JSON.stringify(sentence_json),
  1910. },
  1911. };
  1912. LearnWebSI(MethodName, data).then((res) => {
  1913. this.$set(this.isCollArr, this.curSentIndex, true);
  1914. this.$message.success("收藏成功!");
  1915. });
  1916. } else {
  1917. this.$message.warning("权限不足!");
  1918. }
  1919. },
  1920. //取消收藏
  1921. cancleColl() {
  1922. let text = "";
  1923. this.item.forEach((item) => {
  1924. if (item.chs != "#") {
  1925. text += item.chs;
  1926. }
  1927. });
  1928. let MethodName = "order-collection_manager-CancelMyGoodsCollection_WS";
  1929. let data = {
  1930. goods_type: 502,
  1931. goods_list: [
  1932. {
  1933. goods_id: this.currentTreeID, //课件的id
  1934. sentence_text: text,
  1935. },
  1936. ],
  1937. };
  1938. LearnWebSI(MethodName, data).then((res) => {
  1939. this.$set(this.isCollArr, this.curSentIndex, false);
  1940. this.$message.success("取消成功!");
  1941. });
  1942. },
  1943. //检查收藏状态
  1944. checkCollStatus() {
  1945. let text = "";
  1946. this.item.forEach((item) => {
  1947. if (item.chs != "#") {
  1948. text += item.chs;
  1949. }
  1950. });
  1951. let MethodName = "order-collection_manager-CheckMyGoodsCollectionStatus";
  1952. let data = {
  1953. goods_type: 502,
  1954. goods_id: this.currentTreeID, //课件的id
  1955. sentence_text: text,
  1956. };
  1957. LearnWebSI(MethodName, data).then((res) => {
  1958. let collFlag = res.is_collection == "true" ? true : false;
  1959. this.$set(this.isCollArr, this.curSentIndex, collFlag);
  1960. });
  1961. },
  1962. showPrevNext(bool, key) {
  1963. this[key] = bool;
  1964. },
  1965. prevSentence() {
  1966. if (this.loading) {
  1967. return;
  1968. }
  1969. if (this.curSentIndex == 0) {
  1970. this.$message.warning("已经是第一个句子了");
  1971. return;
  1972. }
  1973. this.curSentIndex = this.curSentIndex - 1;
  1974. this.getSentence();
  1975. if (this.isAuto) {
  1976. this.curTime = this.bg;
  1977. this.$refs.audioLineSent.onTimeupdateTime(this.bg / 1000);
  1978. }
  1979. },
  1980. nextSentence() {
  1981. if (this.loading) {
  1982. return;
  1983. }
  1984. if (this.curSentIndex == this.sentList.length - 1) {
  1985. this.$message.warning("已经是最后一个句子了");
  1986. return;
  1987. }
  1988. this.curSentIndex = this.curSentIndex + 1;
  1989. this.getSentence();
  1990. },
  1991. rollSentence() {
  1992. if (this.curSentIndex == this.sentList.length - 1) {
  1993. this.curSentIndex = 0;
  1994. } else {
  1995. this.curSentIndex = this.curSentIndex + 1;
  1996. }
  1997. this.getSentence();
  1998. },
  1999. changeStatus(key) {
  2000. if (key == "config.isShowEN") {
  2001. if (this.enwords) {
  2002. this[key] = !this[key];
  2003. }
  2004. } else {
  2005. this[key] = !this[key];
  2006. }
  2007. },
  2008. changePinyin() {
  2009. this.$emit("changePinyin");
  2010. },
  2011. changeEN() {
  2012. this.$emit("changeEN");
  2013. },
  2014. setStatus() {
  2015. let _this = this;
  2016. if (_this.key == "isRepeat") {
  2017. if (_this.isRepeat) {
  2018. _this.isRepeat = false;
  2019. _this.isAuto = true;
  2020. _this.key = "isAuto";
  2021. } else {
  2022. _this.isRepeat = true;
  2023. _this.key = "isRepeat";
  2024. }
  2025. } else if (_this.key == "isAuto") {
  2026. if (_this.isAuto) {
  2027. _this.isRepeat = false;
  2028. _this.isAuto = false;
  2029. _this.key = "isRepeat";
  2030. }
  2031. }
  2032. },
  2033. getRerordStatus(bool) {
  2034. this.isShowCompare = bool;
  2035. },
  2036. getMicrophoneStatus(bool) {
  2037. this.isRecording = bool;
  2038. },
  2039. getWavblob(wavblob) {
  2040. this.wavblob = wavblob;
  2041. },
  2042. sentPause(isRecord) {
  2043. this.isRecord = isRecord;
  2044. },
  2045. getCurTime(curTime) {
  2046. let _this = this;
  2047. if (_this.isRepeat) {
  2048. let time = curTime * 1000;
  2049. if (time > _this.ed || time < _this.bg) {
  2050. _this.curTime = _this.bg;
  2051. this.$refs.audioLineSent.onTimeupdateTime(_this.bg / 1000);
  2052. } else {
  2053. _this.curTime = curTime * 1000;
  2054. }
  2055. } else if (_this.isAuto) {
  2056. let time = curTime * 1000;
  2057. if (time > _this.ed) {
  2058. _this.rollSentence();
  2059. _this.curTime = _this.bg;
  2060. _this.$refs.audioLineSent.onTimeupdateTime(_this.bg / 1000);
  2061. } else {
  2062. _this.curTime = curTime * 1000;
  2063. }
  2064. } else {
  2065. _this.curTime = curTime * 1000;
  2066. }
  2067. },
  2068. getCurCompareTime(curTime) {
  2069. let _this = this;
  2070. _this.curTime = curTime * 1000;
  2071. },
  2072. getCurWordTime(curTime) {
  2073. let _this = this;
  2074. _this.curWordTime = curTime * 1000;
  2075. },
  2076. changeBg(bgIndex) {
  2077. this.bgIndex = bgIndex;
  2078. },
  2079. getSentence(type) {
  2080. let _this = this;
  2081. _this.isShowCompare =
  2082. _this.curQue.Bookanswer.practiceModel[_this.curSentIndex] &&
  2083. _this.curQue.Bookanswer.practiceModel[_this.curSentIndex].recordList &&
  2084. _this.curQue.Bookanswer.practiceModel[_this.curSentIndex].recordList
  2085. .length > 0;
  2086. _this.pauseAudio();
  2087. _this.isPlaying = false;
  2088. let item = JSON.parse(JSON.stringify(_this.sentList[_this.curSentIndex]));
  2089. if (item.sentArr) {
  2090. _this.item = item.sentArr;
  2091. _this.enwords = item.enwords;
  2092. } else {
  2093. _this.item = item;
  2094. }
  2095. _this.sentList.forEach((item) => {
  2096. this.isCollArr.push(false);
  2097. });
  2098. _this.bg = _this.curQue.wordTime[_this.curSentIndex].bg;
  2099. _this.ed = _this.curQue.wordTime[_this.curSentIndex].ed;
  2100. let maxTime = (_this.ed - _this.bg) / 1000;
  2101. if (maxTime < 1) {
  2102. _this.maxTime = 1;
  2103. } else {
  2104. _this.maxTime = maxTime;
  2105. }
  2106. if (this.patternType == "录音模式") {
  2107. this.changetime();
  2108. if (this.curQue.Bookanswer.practiceModel[this.curSentIndex]) {
  2109. this.backStatus(true);
  2110. this.curjuzi(
  2111. this.curQue.Bookanswer.practiceModel[this.curSentIndex].recordSrc
  2112. );
  2113. this.LYstatus = "已结束";
  2114. } else {
  2115. this.backStatus(true);
  2116. this.curjuzi();
  2117. this.LYstatus = "未开始";
  2118. }
  2119. } else {
  2120. _this.$nextTick(() => {
  2121. if (_this.$refs.audioLineSent) {
  2122. _this.$refs.audioLineSent.PlayAudio();
  2123. }
  2124. });
  2125. }
  2126. _this.checkCollStatus();
  2127. },
  2128. pauseAudio() {
  2129. let audio = document.getElementsByTagName("audio");
  2130. audio.forEach((item) => {
  2131. item.pause();
  2132. });
  2133. },
  2134. exitFullScreen() {
  2135. // this.patternType = ;
  2136. console.log(this.patternType);
  2137. this.pauseAudio();
  2138. if (this.patternType == "录音模式") {
  2139. this.patternType = "";
  2140. } else {
  2141. this.$emit("exitFullscreen");
  2142. }
  2143. },
  2144. changeFullScreen() {
  2145. this.pauseAudio();
  2146. this.$emit("changeIsFull");
  2147. },
  2148. handleWav(list, tmIndex) {
  2149. tmIndex = tmIndex ? tmIndex : 0;
  2150. this.$emit("handleWav", list, tmIndex);
  2151. },
  2152. // 录音时暂停音频播放
  2153. handleParentPlay() {
  2154. this.stopAudio = true;
  2155. },
  2156. // 音频播放时改变布尔值
  2157. handleChangeStopAudio() {
  2158. this.stopAudio = false;
  2159. },
  2160. //播放音频
  2161. playWord(item) {
  2162. let _this = this;
  2163. _this.stopPlay();
  2164. _this.pauseAudio();
  2165. _this.isWordPlay = false;
  2166. _this.wordIndex = item.wordIndex;
  2167. setTimeout(() => {
  2168. let leg = item.timeList.length;
  2169. _this.wordbg = item.timeList[0].wordBg;
  2170. _this.worded = item.timeList[leg - 1].wordEd;
  2171. let wordMaxTime = (_this.worded - _this.wordbg) / 1000;
  2172. if (wordMaxTime < 1) {
  2173. _this.wordMaxTime = 1;
  2174. } else {
  2175. _this.wordMaxTime = wordMaxTime;
  2176. }
  2177. _this.isWordPlay = true;
  2178. }, 50);
  2179. },
  2180. changePlayStatus() {
  2181. this.isWordPlay = false;
  2182. this.wordIndex = -1;
  2183. },
  2184. showWordDetail(e, item) {
  2185. let _this = this;
  2186. if (_this.TaskModel == "ANSWER") {
  2187. return;
  2188. }
  2189. if (_this.chsFhList.indexOf(item.chs) > -1) {
  2190. return;
  2191. }
  2192. if (_this.oldHz != item.chs) {
  2193. this.isShow = false;
  2194. setTimeout(() => {
  2195. _this.hz = item.chs;
  2196. _this.pinyin = item.pinyin;
  2197. _this.wordIndex = item.wordIndex;
  2198. }, 50);
  2199. }
  2200. _this.clientY = e.clientY;
  2201. let left = e.clientX;
  2202. let width = 0;
  2203. if (item.chs.length == 1 || item.chs.length == 2) {
  2204. width = 304;
  2205. } else if (item.chs.length == 3 || item.chs.length == 4) {
  2206. width = 432;
  2207. } else if (item.chs.length > 3) {
  2208. width = 560;
  2209. }
  2210. // if (left - this.bodyLeft > this.contentWidth / 2) {
  2211. // _this.left = left - width + 10;
  2212. // } else {
  2213. _this.left = left - width / 2;
  2214. //}
  2215. },
  2216. changeWordCard(isShow) {
  2217. let _this = this;
  2218. _this.isShow = isShow;
  2219. _this.oldHz = "";
  2220. _this.hz = "";
  2221. _this.wordIndex = -1;
  2222. },
  2223. // 处理分词数据
  2224. handleNewWords(val) {
  2225. let _this = this;
  2226. _this.isShow = true;
  2227. _this.word = null;
  2228. if (_this.newWordList.indexOf(val) > -1) {
  2229. for (let i = 0; i < this.NNPENewWordList.length; i++) {
  2230. let pItem = this.NNPENewWordList[i];
  2231. for (let j = 0; j < pItem.length; j++) {
  2232. let item = pItem[j];
  2233. if (item.new_word.trim() == val.trim()) {
  2234. let wordlist = val.split("");
  2235. this.word = { list: wordlist, detail: item };
  2236. break;
  2237. }
  2238. }
  2239. }
  2240. } else {
  2241. let wordlist = val.split("");
  2242. let option = {
  2243. definition_list: [],
  2244. mp3_list: [],
  2245. new_word: val,
  2246. pinyin: _this.pinyin,
  2247. };
  2248. _this.word = { list: wordlist, detail: option };
  2249. }
  2250. _this.oldHz = val;
  2251. },
  2252. handleNewword() {
  2253. let NewWordList = [];
  2254. this.NNPENewWordList.forEach((item) => {
  2255. item.forEach((wItem) => {
  2256. NewWordList.push(wItem.new_word);
  2257. });
  2258. });
  2259. this.newWordList = JSON.parse(JSON.stringify(NewWordList));
  2260. },
  2261. getScreenHeight() {
  2262. this.screenHeight = window.innerHeight;
  2263. },
  2264. playMusic(type) {
  2265. let YPindex = this.playList.indexOf("yp");
  2266. let LYindex = this.playList.indexOf("ly");
  2267. if (type == "play") {
  2268. if (LYindex != -1) {
  2269. this.playLY();
  2270. } else {
  2271. if (this.regionData) {
  2272. if (this.xunhunShow) {
  2273. // this.regionData.loop = true;
  2274. // this.regionData.playLoop(); // 循环播放选中区域
  2275. this.wavesurfer_big.play(
  2276. this.regionData.start,
  2277. this.regionData.end
  2278. );
  2279. } else {
  2280. // this.regionData.loop = false;
  2281. this.wavesurfer_big.play(
  2282. this.regionData.start,
  2283. this.regionData.end
  2284. );
  2285. // this.regionData.play();
  2286. }
  2287. } else {
  2288. let time = this.wavesurfer_big.getCurrentTime();
  2289. let start = this.bg / 1000;
  2290. let end = this.ed / 1000;
  2291. if (time * 1000 == this.ed || this.curTime * 1000 == 0) {
  2292. this.wavesurfer_big.play(start, end);
  2293. } else {
  2294. this.wavesurfer_big.play(this.curTime / 1000, end);
  2295. }
  2296. }
  2297. }
  2298. } else {
  2299. if (LYindex != -1) {
  2300. wavesurfer_ly.pause();
  2301. } else {
  2302. this.wavesurfer_big.pause();
  2303. }
  2304. }
  2305. },
  2306. },
  2307. //生命周期 - 创建完成(可以访问当前this实例)
  2308. created() {},
  2309. //生命周期 - 挂载完成(可以访问DOM元素)
  2310. mounted() {
  2311. // this.startCanvas();
  2312. let _this = this;
  2313. $(window).resize(() => {
  2314. _this.getScreenHeight();
  2315. });
  2316. _this.getScreenHeight();
  2317. document.addEventListener("keyup", function (e) {
  2318. if (_this.isKeyboard) {
  2319. if (e.keyCode == 32) {
  2320. //空格
  2321. if (_this.patternType == "录音模式") {
  2322. if (!_this.isPlaying) {
  2323. _this.playMusic("play");
  2324. } else {
  2325. _this.playMusic("puse");
  2326. }
  2327. } else {
  2328. _this.$nextTick(() => {
  2329. if (_this.$refs.audioLineSent) {
  2330. _this.$refs.audioLineSent.PlayAudio();
  2331. }
  2332. });
  2333. }
  2334. } else if (e.keyCode == 38) {
  2335. _this.prevSentence();
  2336. } else if (e.keyCode == 40) {
  2337. _this.nextSentence();
  2338. } else if (e.keyCode == 13) {
  2339. // _this.$nextTick(() => {
  2340. // _this.$refs.Soundrecorddiff.microphone();
  2341. // });
  2342. }
  2343. }
  2344. });
  2345. if (_this.NNPENewWordList) {
  2346. _this.handleNewword();
  2347. }
  2348. _this.curSentIndex = _this.sentIndex;
  2349. _this.getSentence("first");
  2350. document.addEventListener("fullscreenchange", () => {
  2351. let isFullscreen =
  2352. document.fullscreenElement ||
  2353. document.mozFullScreenElement ||
  2354. document.webkitFullscreenElement ||
  2355. document.fullScreen ||
  2356. document.mozFullScreen ||
  2357. document.webkitIsFullScreen;
  2358. if (!isFullscreen) {
  2359. _this.changeFullScreen();
  2360. }
  2361. });
  2362. document.addEventListener("mozfullscreenchange", () => {
  2363. let isFullscreen =
  2364. document.fullscreenElement ||
  2365. document.mozFullScreenElement ||
  2366. document.webkitFullscreenElement ||
  2367. document.fullScreen ||
  2368. document.mozFullScreen ||
  2369. document.webkitIsFullScreen;
  2370. if (!isFullscreen) {
  2371. _this.changeFullScreen();
  2372. }
  2373. });
  2374. document.addEventListener("webkitfullscreenchange", () => {
  2375. let isFullscreen =
  2376. document.fullscreenElement ||
  2377. document.mozFullScreenElement ||
  2378. document.webkitFullscreenElement ||
  2379. document.fullScreen ||
  2380. document.mozFullScreen ||
  2381. document.webkitIsFullScreen;
  2382. if (!isFullscreen) {
  2383. _this.changeFullScreen();
  2384. }
  2385. });
  2386. document.addEventListener("msfullscreenchange", () => {
  2387. let isFullscreen =
  2388. document.fullscreenElement ||
  2389. document.mozFullScreenElement ||
  2390. document.webkitFullscreenElement ||
  2391. document.fullScreen ||
  2392. document.mozFullScreen ||
  2393. document.webkitIsFullScreen;
  2394. if (!isFullscreen) {
  2395. _this.changeFullScreen();
  2396. }
  2397. });
  2398. },
  2399. beforeCreate() {}, //生命周期 - 创建之前
  2400. beforeMount() {}, //生命周期 - 挂载之前
  2401. beforeUpdate() {}, //生命周期 - 更新之前
  2402. updated() {}, //生命周期 - 更新之后
  2403. beforeDestroy() {}, //生命周期 - 销毁之前
  2404. destroyed() {
  2405. clearInterval(this.timer);
  2406. this.timer = null;
  2407. }, //生命周期 - 销毁完成
  2408. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  2409. };
  2410. </script>
  2411. <style lang='scss' scoped>
  2412. //@import url(); 引入公共css类
  2413. .waveform-wrapper {
  2414. .big {
  2415. display: flex;
  2416. .big_dv2 {
  2417. flex: 1;
  2418. }
  2419. }
  2420. .ly {
  2421. .play_erji {
  2422. margin-top: 0;
  2423. }
  2424. .erji {
  2425. margin-top: 0;
  2426. }
  2427. }
  2428. .play_erji {
  2429. // margin-top: 16px;
  2430. width: 64px;
  2431. height: 130px;
  2432. display: flex;
  2433. justify-content: center;
  2434. align-items: center;
  2435. background: #4f92f6;
  2436. cursor: pointer;
  2437. img {
  2438. width: 28px;
  2439. height: 28px;
  2440. }
  2441. }
  2442. .erji {
  2443. // margin-top: 16px;
  2444. width: 64px;
  2445. height: 130px;
  2446. display: flex;
  2447. justify-content: center;
  2448. align-items: center;
  2449. background: #ffffff;
  2450. cursor: pointer;
  2451. img {
  2452. width: 28px;
  2453. height: 28px;
  2454. }
  2455. }
  2456. }
  2457. #waveform_ly {
  2458. background: #141414;
  2459. }
  2460. #waveform_big {
  2461. background: #f3f3f48e;
  2462. }
  2463. #waveform {
  2464. background: #f3f3f48e;
  2465. border-radius: 8px;
  2466. width: 92%;
  2467. margin: 0 auto;
  2468. }
  2469. .cuttentime {
  2470. font-weight: 400;
  2471. font-size: 32px;
  2472. line-height: 150%;
  2473. color: #000000;
  2474. text-align: center;
  2475. }
  2476. .operate {
  2477. display: flex;
  2478. align-items: center;
  2479. justify-content: center;
  2480. .gray_speed {
  2481. opacity: 0.3;
  2482. }
  2483. .speed {
  2484. width: 48px;
  2485. height: 48px;
  2486. margin-right: 44px;
  2487. text-align: center;
  2488. line-height: 48px;
  2489. cursor: pointer;
  2490. position: relative;
  2491. .speedList {
  2492. position: absolute;
  2493. top: -177px;
  2494. width: 52px;
  2495. background: #ffffff;
  2496. border: 1px solid rgba(0, 0, 0, 0.08);
  2497. border-radius: 4px;
  2498. z-index: 5;
  2499. padding: 2px 0;
  2500. > div:hover {
  2501. background: #e9e9e9;
  2502. border-radius: 4px;
  2503. }
  2504. > div {
  2505. padding: 4px 2px;
  2506. width: 44px;
  2507. height: 26px;
  2508. text-align: right;
  2509. margin: 1px 4px;
  2510. font-weight: 400;
  2511. font-size: 14px;
  2512. line-height: 22px;
  2513. }
  2514. }
  2515. }
  2516. .speed_sele {
  2517. background: #e6e6e6;
  2518. border-radius: 4px;
  2519. }
  2520. img {
  2521. cursor: pointer;
  2522. margin-right: 44px;
  2523. }
  2524. }
  2525. .voicefull {
  2526. width: 100%;
  2527. height: 100vh;
  2528. overflow: hidden;
  2529. display: flex;
  2530. flex-direction: column;
  2531. .NNPE-wordDetail {
  2532. position: fixed;
  2533. z-index: 9999;
  2534. }
  2535. &.bg1 {
  2536. background: #fff;
  2537. }
  2538. &.bg2 {
  2539. background: linear-gradient(180deg, #274533 0%, #385f45 100%);
  2540. }
  2541. &-top {
  2542. height: 136px;
  2543. width: 100%;
  2544. box-sizing: border-box;
  2545. padding: 0 40px;
  2546. .voicefull-top-hidden {
  2547. width: 100%;
  2548. height: 136px;
  2549. visibility: hidden;
  2550. display: flex;
  2551. justify-content: space-between;
  2552. align-items: center;
  2553. }
  2554. .voicefull-top-show {
  2555. width: 100%;
  2556. height: 136px;
  2557. visibility: visible;
  2558. display: flex;
  2559. justify-content: space-between;
  2560. align-items: center;
  2561. }
  2562. .top-left {
  2563. display: flex;
  2564. justify-content: flex-start;
  2565. align-items: center;
  2566. }
  2567. .select-bg {
  2568. display: flex;
  2569. justify-content: space-between;
  2570. align-items: center;
  2571. width: 96px;
  2572. height: 56px;
  2573. border: 1px solid rgba(0, 0, 0, 0.1);
  2574. border-radius: 40px;
  2575. display: flex;
  2576. justify-content: center;
  2577. align-items: center;
  2578. box-sizing: border-box;
  2579. margin-right: 32px;
  2580. &.select-bg-blue {
  2581. background: rgba(255, 255, 255, 0.1);
  2582. border: 1px solid rgba(0, 0, 0, 0.1);
  2583. }
  2584. > div {
  2585. width: 36px;
  2586. height: 36px;
  2587. border-radius: 100%;
  2588. display: flex;
  2589. justify-content: center;
  2590. align-items: center;
  2591. &.bg-white-box {
  2592. background: 0 0;
  2593. margin-right: 4px;
  2594. &.active {
  2595. background: #de4444;
  2596. }
  2597. }
  2598. &.bg-green-box {
  2599. background: #fff;
  2600. &.active {
  2601. background: #ffc600;
  2602. }
  2603. }
  2604. > span {
  2605. width: 24px;
  2606. height: 24px;
  2607. border-radius: 100%;
  2608. box-sizing: border-box;
  2609. cursor: pointer;
  2610. &.bg-white {
  2611. background: #fff;
  2612. }
  2613. &.bg-green {
  2614. background: linear-gradient(180deg, #274533 0%, #385f45 100%);
  2615. }
  2616. }
  2617. }
  2618. }
  2619. .set-fontSize {
  2620. padding: 0 20px;
  2621. height: 56px;
  2622. background: #ffffff;
  2623. border: 1px solid rgba(0, 0, 0, 0.1);
  2624. border-radius: 40px;
  2625. display: flex;
  2626. justify-content: center;
  2627. align-items: center;
  2628. &-green {
  2629. background: rgba(255, 255, 255, 0.1);
  2630. border: 1px solid rgba(0, 0, 0, 0.1);
  2631. }
  2632. > span {
  2633. width: 24px;
  2634. height: 24px;
  2635. margin: 0 4px;
  2636. &.font-jian {
  2637. &-black {
  2638. background: url("../../../assets/NPC/jian-black.png") no-repeat left
  2639. top;
  2640. background-size: 100% 100%;
  2641. cursor: pointer;
  2642. }
  2643. &-yellow {
  2644. background: url("../../../assets/NPC/jian-white.png") no-repeat left
  2645. top;
  2646. background-size: 100% 100%;
  2647. cursor: pointer;
  2648. }
  2649. &-white-disabled {
  2650. background: url("../../../assets/NPC/jian-white-disabled.png")
  2651. no-repeat left top;
  2652. background-size: 100% 100%;
  2653. cursor: pointer;
  2654. }
  2655. &-yellow-disabled {
  2656. background: url("../../../assets/NPC/jian-yellow-disabled.png")
  2657. no-repeat left top;
  2658. background-size: 100% 100%;
  2659. cursor: pointer;
  2660. }
  2661. }
  2662. &.font-img {
  2663. &-black {
  2664. background: url("../../../assets/NPC/fontSize-black.png") no-repeat
  2665. left top;
  2666. background-size: 100% 100%;
  2667. }
  2668. &-yellow {
  2669. background: url("../../../assets/NPC/fontSize-white.png") no-repeat
  2670. left top;
  2671. background-size: 100% 100%;
  2672. }
  2673. }
  2674. &.font-jia {
  2675. &-black {
  2676. background: url("../../../assets/NPC/jia-black.png") no-repeat left
  2677. top;
  2678. background-size: 100% 100%;
  2679. cursor: pointer;
  2680. }
  2681. &-yellow {
  2682. background: url("../../../assets/NPC/jia-white.png") no-repeat left
  2683. top;
  2684. background-size: 100% 100%;
  2685. cursor: pointer;
  2686. }
  2687. &-white-disabled {
  2688. background: url("../../../assets/NPC/jia-white-disabled.png")
  2689. no-repeat left top;
  2690. background-size: 100% 100%;
  2691. cursor: pointer;
  2692. }
  2693. &-yellow-disabled {
  2694. background: url("../../../assets/NPC/jia-yellow-disabled.png")
  2695. no-repeat left top;
  2696. background-size: 100% 100%;
  2697. cursor: pointer;
  2698. }
  2699. }
  2700. }
  2701. }
  2702. .top-middle {
  2703. display: flex;
  2704. justify-content: center;
  2705. align-items: center;
  2706. .audio-box {
  2707. width: 56px;
  2708. height: 56px;
  2709. background: #ffffff;
  2710. border: 1px solid rgba(0, 0, 0, 0.1);
  2711. border-radius: 40px;
  2712. display: flex;
  2713. justify-content: center;
  2714. align-items: center;
  2715. &-green {
  2716. background: rgba(255, 255, 255, 0.1);
  2717. border: 1px solid rgba(0, 0, 0, 0.1);
  2718. }
  2719. }
  2720. }
  2721. }
  2722. .op-btn {
  2723. width: 56px;
  2724. height: 56px;
  2725. border-radius: 100%;
  2726. display: flex;
  2727. justify-content: center;
  2728. align-items: center;
  2729. cursor: pointer;
  2730. margin-left: 32px;
  2731. background: #ffffff;
  2732. border: 1px solid rgba(0, 0, 0, 0.1);
  2733. box-sizing: border-box;
  2734. &-green {
  2735. background: rgba(255, 255, 255, 0.1);
  2736. border: 1px solid rgba(0, 0, 0, 0.1);
  2737. }
  2738. &.close-btn {
  2739. background: #274533;
  2740. border: 1px solid rgba(0, 0, 0, 0.1);
  2741. }
  2742. > span {
  2743. width: 24px;
  2744. height: 24px;
  2745. &.close-icon {
  2746. background: url("../../../assets/icon/cross-24-normal-black.png")
  2747. no-repeat left top;
  2748. background-size: 100% 100%;
  2749. &-white {
  2750. background: url("../../../assets/icon/cross-24-normal-white.png")
  2751. no-repeat left top;
  2752. background-size: 100% 100%;
  2753. }
  2754. }
  2755. }
  2756. }
  2757. .repeat-icon {
  2758. background: url("../../../assets/icon/Repeat-24-normal-red.png") no-repeat
  2759. left top;
  2760. background-size: 100% 100%;
  2761. &.disabled {
  2762. background: url("../../../assets/icon/Repeat-24-disable-Black.png")
  2763. no-repeat left top;
  2764. background-size: 100% 100%;
  2765. }
  2766. &-yellow {
  2767. background: url("../../../assets/icon/Repeat-24-normal-yellow.png")
  2768. no-repeat left top;
  2769. background-size: 100% 100%;
  2770. }
  2771. &.auto-icon {
  2772. background: url("../../../assets/icon/Auto-24-next-red.png") no-repeat
  2773. left top;
  2774. background-size: 100% 100%;
  2775. &-yellow {
  2776. background: url("../../../assets/icon/Auto-24-next-yellow.png")
  2777. no-repeat left top;
  2778. background-size: 100% 100%;
  2779. }
  2780. }
  2781. }
  2782. .pinyin-icon {
  2783. background: url("../../../assets/icon/pinyin-24-normal-red.png") no-repeat
  2784. left top;
  2785. background-size: 100% 100%;
  2786. &.disabled {
  2787. background: url("../../../assets/icon/pinyin-24-disable-Black.png")
  2788. no-repeat left top;
  2789. background-size: 100% 100%;
  2790. }
  2791. &-yellow {
  2792. background: url("../../../assets/icon/pinyin-24-normal-yellow.png")
  2793. no-repeat left top;
  2794. background-size: 100% 100%;
  2795. }
  2796. }
  2797. .en-icon {
  2798. background: url("../../../assets/icon/EN-24-normal-Red.png") no-repeat left
  2799. top;
  2800. background-size: 100% 100%;
  2801. &.disabled {
  2802. background: url("../../../assets/icon/EN-24-disable-Black.png") no-repeat
  2803. left top;
  2804. background-size: 100% 100%;
  2805. }
  2806. &-yellow {
  2807. background: url("../../../assets/icon/EN-24-normal-yellow.png") no-repeat
  2808. left top;
  2809. background-size: 100% 100%;
  2810. }
  2811. }
  2812. .coll-icon {
  2813. background: url("../../../assets/icon/bookmarkfill-24-normal-red.png")
  2814. no-repeat left top;
  2815. background-size: 100% 100%;
  2816. &.disabled {
  2817. background: url("../../../assets/icon/bookmarkfill-24-disable-Black.png")
  2818. no-repeat left top;
  2819. background-size: 100% 100%;
  2820. }
  2821. &-yellow {
  2822. background: url("../../../assets/icon/bookmarkfill-24-normal-yellow.png")
  2823. no-repeat left top;
  2824. background-size: 100% 100%;
  2825. }
  2826. }
  2827. .keyboard-icon {
  2828. background: url("../../../assets/icon/enter-24-keyboard-red.png") no-repeat
  2829. left top;
  2830. background-size: 100% 100%;
  2831. &.disabled {
  2832. background: url("../../../assets/icon/enter-24-keyboard-disable-Black.png")
  2833. no-repeat left top;
  2834. background-size: 100% 100%;
  2835. }
  2836. &-yellow {
  2837. background: url("../../../assets/icon/enter-24-keyboard-yellow.png")
  2838. no-repeat left top;
  2839. background-size: 100% 100%;
  2840. }
  2841. }
  2842. &-content {
  2843. flex: 1;
  2844. width: 100%;
  2845. box-sizing: border-box;
  2846. display: flex;
  2847. align-items: center;
  2848. .vc-box {
  2849. padding: 0 8px 0 36px;
  2850. &-right {
  2851. padding: 0 36px 0 8px;
  2852. }
  2853. }
  2854. .vc-left {
  2855. width: 64px;
  2856. height: 64px;
  2857. cursor: pointer;
  2858. &-grey {
  2859. background: url("../../../assets/NPC/left-grey.png") no-repeat left top;
  2860. background-size: 100% 100%;
  2861. }
  2862. &-black {
  2863. background: url("../../../assets/NPC/left-black.png") no-repeat left top;
  2864. background-size: 100% 100%;
  2865. }
  2866. &-white {
  2867. background: url("../../../assets/NPC/left-white.png") no-repeat left top;
  2868. background-size: 100% 100%;
  2869. }
  2870. &.hidden {
  2871. visibility: hidden;
  2872. }
  2873. }
  2874. .vc-right {
  2875. width: 64px;
  2876. height: 64px;
  2877. cursor: pointer;
  2878. &-grey {
  2879. background: url("../../../assets/NPC/right-grey.png") no-repeat left top;
  2880. background-size: 100% 100%;
  2881. }
  2882. &-black {
  2883. background: url("../../../assets/NPC/right-black.png") no-repeat left
  2884. top;
  2885. background-size: 100% 100%;
  2886. }
  2887. &-white {
  2888. background: url("../../../assets/NPC/right-white.png") no-repeat left
  2889. top;
  2890. background-size: 100% 100%;
  2891. }
  2892. }
  2893. .vc-main {
  2894. width: fit-content;
  2895. margin: 0 auto;
  2896. padding: 0 67px;
  2897. .enwords {
  2898. padding: 0 3px;
  2899. margin-top: 24px;
  2900. color: rgba(0, 0, 0, 0.45);
  2901. font-size: 24px;
  2902. line-height: 32px;
  2903. font-family: "robot";
  2904. &-green {
  2905. color: rgba(255, 255, 255, 0.65);
  2906. }
  2907. }
  2908. }
  2909. .NNPE-words {
  2910. float: left;
  2911. user-select: none;
  2912. -webkit-user-select: none;
  2913. -moz-user-select: none;
  2914. -ms-user-select: none;
  2915. &-box {
  2916. float: left;
  2917. > span {
  2918. display: block;
  2919. &.NNPE-pinyin {
  2920. font-family: "GB-PINYINOK-B";
  2921. font-weight: normal;
  2922. font-size: 32px;
  2923. line-height: 1.25;
  2924. box-sizing: border-box;
  2925. color: rgba(0, 0, 0, 0.85);
  2926. &.bottom {
  2927. padding-bottom: 16px;
  2928. }
  2929. &.noFont {
  2930. font-family: initial;
  2931. }
  2932. &.textLeft {
  2933. text-align: left;
  2934. }
  2935. &.font-white {
  2936. color: #fff;
  2937. }
  2938. &.wordBlank {
  2939. color: rgba(0, 0, 0, 0.85);
  2940. }
  2941. }
  2942. &.NNPE-chs {
  2943. font-family: "FZJCGFKTK";
  2944. font-size: 48px;
  2945. line-height: 1.17;
  2946. color: rgba(0, 0, 0, 0.85);
  2947. &.bottom {
  2948. padding-bottom: 16px;
  2949. }
  2950. .font-white {
  2951. color: #fff;
  2952. }
  2953. .active {
  2954. color: #de4444;
  2955. &-yellow {
  2956. color: #ffc600;
  2957. }
  2958. }
  2959. .wordActive {
  2960. color: #de4444;
  2961. }
  2962. .wordActive-blue {
  2963. color: #ffc600;
  2964. }
  2965. }
  2966. // &.padding {
  2967. // padding-right: 6px;
  2968. // }
  2969. }
  2970. }
  2971. &.textLeft {
  2972. text-align: left;
  2973. }
  2974. &.textCenter {
  2975. text-align: center;
  2976. }
  2977. &.textRight {
  2978. text-align: right;
  2979. }
  2980. > span {
  2981. display: block;
  2982. &.NNPE-pinyin {
  2983. font-family: "GB-PINYINOK-B";
  2984. font-weight: normal;
  2985. font-size: 32px;
  2986. line-height: 1.25;
  2987. box-sizing: border-box;
  2988. color: rgba(0, 0, 0, 0.85);
  2989. &.bottom {
  2990. padding-bottom: 16px;
  2991. }
  2992. &.font-white {
  2993. color: #fff;
  2994. }
  2995. &.noFont {
  2996. font-family: initial;
  2997. }
  2998. &.textLeft {
  2999. text-align: left;
  3000. }
  3001. &.wordBlank {
  3002. color: rgba(0, 0, 0, 0.85);
  3003. }
  3004. }
  3005. &.NNPE-chs {
  3006. font-family: "FZJCGFKTK";
  3007. font-size: 48px;
  3008. line-height: 1.17;
  3009. color: rgba(0, 0, 0, 0.85);
  3010. &.bottom {
  3011. padding-bottom: 16px;
  3012. }
  3013. .font-white {
  3014. color: #fff;
  3015. }
  3016. .active {
  3017. color: #de4444;
  3018. &-yellow {
  3019. color: #ffc600;
  3020. }
  3021. }
  3022. .wordActive {
  3023. color: #de4444;
  3024. }
  3025. .wordActive-blue {
  3026. color: #ffc600;
  3027. }
  3028. }
  3029. &.padding {
  3030. padding-left: 3px;
  3031. padding-right: 3px;
  3032. }
  3033. }
  3034. }
  3035. }
  3036. &-bottom {
  3037. height: 136px;
  3038. width: 100%;
  3039. box-sizing: border-box;
  3040. display: flex;
  3041. justify-content: space-between;
  3042. align-items: center;
  3043. padding-right: 40px;
  3044. .voicefull-bottom-show {
  3045. height: 136px;
  3046. width: 100%;
  3047. display: flex;
  3048. justify-content: space-between;
  3049. align-items: center;
  3050. visibility: visible;
  3051. }
  3052. .voicefull-bottom-hidden {
  3053. height: 136px;
  3054. width: 100%;
  3055. display: flex;
  3056. justify-content: space-between;
  3057. align-items: center;
  3058. visibility: hidden;
  3059. }
  3060. .bottom-left {
  3061. display: flex;
  3062. justify-content: flex-start;
  3063. align-items: center;
  3064. padding-left: 40px;
  3065. .darcColor_pattern {
  3066. background: rgba(255, 255, 255, 0.1) !important;
  3067. border: 1px solid rgba(0, 0, 0, 0.1) !important;
  3068. }
  3069. .pattern {
  3070. width: 56px;
  3071. height: 56px;
  3072. background: #ffffff;
  3073. border: 1px solid rgba(0, 0, 0, 0.1);
  3074. border-radius: 40px;
  3075. cursor: pointer;
  3076. display: flex;
  3077. justify-content: center;
  3078. align-items: center;
  3079. img {
  3080. width: 24px;
  3081. height: 24px;
  3082. }
  3083. }
  3084. > img {
  3085. width: 72px;
  3086. height: 48px;
  3087. cursor: pointer;
  3088. }
  3089. &-margin {
  3090. margin-left: 40px;
  3091. }
  3092. .compare-box {
  3093. height: 56px;
  3094. padding: 16px 16px;
  3095. box-sizing: border-box;
  3096. border: 1px solid rgba(0, 0, 0, 0.1);
  3097. border-radius: 0 40px 40px 0;
  3098. border-left: 0px solid rgba(0, 0, 0, 0.1);
  3099. &-white {
  3100. background: rgba(255, 255, 255, 0.1);
  3101. border: 1px solid rgba(0, 0, 0, 0.1);
  3102. border-left: 0;
  3103. }
  3104. &-answer {
  3105. border-radius: 40px;
  3106. }
  3107. }
  3108. }
  3109. .page-count {
  3110. padding: 8px;
  3111. font-size: 16px;
  3112. line-height: 24px;
  3113. font-family: "robot";
  3114. color: #000000;
  3115. min-width: 60px;
  3116. box-sizing: border-box;
  3117. border-radius: 8px;
  3118. background: #fff;
  3119. text-align: center;
  3120. display: flex;
  3121. align-items: center;
  3122. > div {
  3123. margin-left: 22px;
  3124. img {
  3125. cursor: pointer;
  3126. }
  3127. }
  3128. &-green {
  3129. color: #ffffff;
  3130. background: rgba(255, 255, 255, 0.2);
  3131. }
  3132. }
  3133. }
  3134. }
  3135. .word-play-audio {
  3136. position: absolute;
  3137. left: -1000px;
  3138. }
  3139. </style>
  3140. <style lang="scss">
  3141. #waveform_big {
  3142. > :nth-child(1) {
  3143. overflow-x: scroll;
  3144. }
  3145. }
  3146. #waveform_ly {
  3147. > :nth-child(1) {
  3148. overflow-x: scroll;
  3149. }
  3150. }
  3151. .NPC-Big-Book-preview-green {
  3152. .bg1 {
  3153. .repeat-icon {
  3154. background: url("../../../assets/icon/Repeat-24-normal-Green.png")
  3155. no-repeat left top;
  3156. background-size: 100% 100%;
  3157. }
  3158. .pinyin-icon {
  3159. background: url("../../../assets/icon/pinyin-24-normal-green.png")
  3160. no-repeat left top;
  3161. background-size: 100% 100%;
  3162. }
  3163. .en-icon {
  3164. background: url("../../../assets/icon/EN-24-normal-Green.png") no-repeat
  3165. left top;
  3166. background-size: 100% 100%;
  3167. }
  3168. .coll-icon {
  3169. background: url("../../../assets/icon/bookmarkfill-24-normal-green.png")
  3170. no-repeat left top;
  3171. background-size: 100% 100%;
  3172. }
  3173. }
  3174. }
  3175. .NPC-Big-Book-preview-brown {
  3176. .bg1 {
  3177. .repeat-icon {
  3178. background: url("../../../assets/icon/Repeat-24-normal-Brown.png")
  3179. no-repeat left top;
  3180. background-size: 100% 100%;
  3181. }
  3182. .pinyin-icon {
  3183. background: url("../../../assets/icon/pinyin-24-normal-brown.png")
  3184. no-repeat left top;
  3185. background-size: 100% 100%;
  3186. }
  3187. .en-icon {
  3188. background: url("../../../assets/icon/EN-24-normal-Brown.png") no-repeat
  3189. left top;
  3190. background-size: 100% 100%;
  3191. }
  3192. .coll-icon {
  3193. background: url("../../../assets/icon/bookmarkfill-24-normal-brown.png")
  3194. no-repeat left top;
  3195. background-size: 100% 100%;
  3196. }
  3197. }
  3198. }
  3199. </style>