Voicefullscreen-ly.vue 110 KB

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