AudioLineSentence.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. <template>
  2. <!-- mp3Source && mp3Source == 'tts' ? 'Audio-tts' : '', -->
  3. <div :class="['Audio', 'AudioFull']">
  4. <div
  5. :class="['audioLine3', bgIndex == 1 ? 'audioLine3-green' : '']"
  6. @click="PlayAudio"
  7. >
  8. <div
  9. class="play"
  10. :class="[
  11. audio.loading ? 'loadBtn' : audio.playing ? 'playBtn' : 'pauseBtn',
  12. ]"
  13. />
  14. </div>
  15. <!-- <div :class="['audioLine', bgIndex == 1 ? 'audioLine-green' : '']">
  16. <div
  17. class="play"
  18. :class="[
  19. audio.loading ? 'loadBtn' : audio.playing ? 'playBtn' : 'pauseBtn',
  20. ]"
  21. @click="PlayAudio"
  22. />
  23. <template >
  24. <el-slider
  25. v-model="playValue"
  26. :style="{ width: sliderWidth + 'px', height: '6px' }"
  27. :format-tooltip="formatProcessToolTip"
  28. @change="changeCurrentTime"
  29. />
  30. <span :class="bgIndex == 1 ? 'color-white' : ''">
  31. {{ realFormatSecond(audio.maxTime) }}
  32. </span>
  33. </template>
  34. </div> -->
  35. <!-- <div v-else class="audioLine2">
  36. <div
  37. class="play-icon"
  38. :class="
  39. audio.loading
  40. ? 'loadBtn'
  41. : audio.playing
  42. ? 'playBtn-icon'
  43. : 'pauseBtn-icon'
  44. "
  45. @click="PlayAudio"
  46. />
  47. </div> -->
  48. <audio
  49. :id="audioId"
  50. :ref="audioId"
  51. :src="mp3"
  52. preload="meta"
  53. @loadedmetadata="onLoadedmetadata"
  54. @timeupdate="onTimeupdate"
  55. @canplaythrough="oncanplaythrough"
  56. />
  57. </div>
  58. </template>
  59. <script>
  60. // 这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  61. // 例如:import 《组件名称》from ‘《组件路径》';
  62. export default {
  63. // import引入的组件需要注入到对象中才能使用
  64. components: {},
  65. props: [
  66. "mp3",
  67. "mp3Source",
  68. "getCurTime",
  69. "stopAudio",
  70. "width",
  71. "isRepeat",
  72. "themeColor",
  73. "ed",
  74. "bg",
  75. "audioId",
  76. "maxTime",
  77. "hideSlider",
  78. "bgIndex",
  79. "wordPlay",
  80. "isAuto",
  81. "curTime",
  82. ],
  83. data() {
  84. // 这里存放数据
  85. return {
  86. playValue: 0,
  87. audio: {
  88. // 该字段是音频是否处于播放状态的属性
  89. playing: false,
  90. // 音频当前播放时长
  91. currentTime: 0,
  92. // 音频最大播放时长
  93. maxTime: 0,
  94. isPlaying: false,
  95. loading: false,
  96. },
  97. playTime: parseInt(this.maxTime),
  98. audioAllTime: null, // 展示总时间
  99. duioCurrentTime: null, // 剩余时间
  100. count: 0,
  101. loading: null,
  102. playcount: 0,
  103. };
  104. },
  105. // 计算属性 类似于data概念
  106. computed: {
  107. sliderWidth() {
  108. let width = 0;
  109. if (this.width) {
  110. width = this.width;
  111. } else {
  112. width = 662;
  113. }
  114. return width;
  115. },
  116. },
  117. // 监控data中数据变化
  118. watch: {
  119. stopAudio: {
  120. handler(val, oldVal) {
  121. const _this = this;
  122. if (val) {
  123. _this.$refs[_this.audioId].pause();
  124. _this.audio.playing = false;
  125. }
  126. },
  127. // 深度观察监听
  128. deep: true,
  129. },
  130. "audio.playing": {
  131. handler(val) {
  132. this.$emit("playChange", val);
  133. if (val) this.$emit("handleChangeStopAudio");
  134. },
  135. },
  136. },
  137. // 生命周期 - 创建完成(可以访问当前this实例)
  138. created() {},
  139. // 生命周期 - 挂载完成(可以访问DOM元素)
  140. mounted() {
  141. let _this = this;
  142. _this.$nextTick(() => {
  143. if (_this.wordPlay) {
  144. _this.PlayAudio();
  145. }
  146. if (_this.isAuto) {
  147. _this.PlayAudio();
  148. }
  149. });
  150. let audioId = _this.audioId;
  151. _this.$refs[audioId].addEventListener("loadstart", function () {});
  152. _this.$refs[audioId].addEventListener("play", function () {
  153. _this.audio.playing = true;
  154. _this.audio.loading = false;
  155. });
  156. _this.$refs[audioId].addEventListener("pause", function () {
  157. _this.audio.playing = false;
  158. if (_this.audio.currentTime * 1000 + 500 > _this.ed) {
  159. //_this.$emit("sentPause", true);
  160. _this.playValue = 0;
  161. _this.audio.isPlaying = false;
  162. }
  163. if (_this.wordPlay) {
  164. _this.$emit("changePlayStatus");
  165. _this.audio.isPlaying = false;
  166. }
  167. });
  168. _this.$refs[audioId].addEventListener("ended", function () {
  169. _this.audio.playing = false;
  170. _this.audio.isPlaying = false;
  171. _this.$emit("handleListenRead", false);
  172. });
  173. this.$nextTick(() => {
  174. document
  175. .getElementsByClassName("el-slider__button-wrapper")[0]
  176. .addEventListener("mousedown", function () {
  177. _this.$refs[audioId].pause();
  178. _this.audio.playing = false;
  179. });
  180. });
  181. },
  182. // 生命周期-挂载之前
  183. beforeMount() {},
  184. // 生命周期-更新之后
  185. updated() {},
  186. // 如果页面有keep-alive缓存功能,这个函数会触发
  187. activated() {},
  188. // 方法集合
  189. methods: {
  190. PlayAudio() {
  191. let audioId = this.audioId;
  192. let audio = document.getElementsByTagName("audio");
  193. audio.forEach((item) => {
  194. if (item.src == this.mp3) {
  195. if (item.id !== audioId) {
  196. item.pause();
  197. }
  198. } else {
  199. item.pause();
  200. }
  201. });
  202. let video = document.getElementsByTagName("video");
  203. video.forEach((vItem) => {
  204. vItem.pause();
  205. });
  206. this.$set(this.audio, "isPlaying", true);
  207. if (this.audio.playing) {
  208. this.$refs[audioId].pause();
  209. this.audio.playing = false;
  210. this.$emit("handleListenRead", false);
  211. } else {
  212. if (this.count == 0) {
  213. this.audio.loading = true;
  214. this.count++;
  215. }
  216. this.$refs[audioId].play();
  217. if (this.playcount == 0) {
  218. this.onTimeupdateTime(this.bg / 1000);
  219. this.playcount++;
  220. }
  221. this.$emit("handleChangeStopAudio");
  222. this.$emit("handleListenRead", true);
  223. }
  224. },
  225. oncanplaythrough() {
  226. let _this = this;
  227. // setTimeout(() => {
  228. _this.audio.loading = false;
  229. // }, 10000);
  230. },
  231. // 点击 拖拽播放音频
  232. changeCurrentTime(value) {
  233. let audioId = this.audioId;
  234. this.$refs[audioId].play();
  235. this.audio.playing = true;
  236. this.$refs[audioId].currentTime = parseInt(
  237. (value / 100) * this.audio.maxTime
  238. );
  239. },
  240. mousedown() {
  241. let audioId = this.audioId;
  242. this.$refs[audioId].pause();
  243. this.audio.playing = false;
  244. },
  245. // 进度条格式化toolTip
  246. formatProcessToolTip(index) {
  247. index = parseInt((this.audio.maxTime / 100) * index);
  248. return this.realFormatSecond(index);
  249. },
  250. // 音频加载完之后
  251. onLoadedmetadata(res) {
  252. this.audio.maxTime = parseInt(this.maxTime);
  253. //this.audio.maxTime = parseInt(res.target.duration);
  254. //this.playTime = parseInt(this.maxTime);
  255. //this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
  256. },
  257. // 当音频当前时间改变后,进度条也要改变
  258. onTimeupdate(res) {
  259. let _this = this;
  260. let audioId = _this.audioId;
  261. _this.audio.currentTime = res.target.currentTime;
  262. _this.getCurTime(res.target.currentTime);
  263. let time = _this.audio.currentTime - _this.bg / 1000;
  264. _this.playValue = (time / _this.audio.maxTime) * 100;
  265. setTimeout(() => {
  266. if (_this.audio.currentTime * 1000 > _this.ed) {
  267. if (_this.$refs[audioId]) {
  268. _this.$refs[audioId].pause();
  269. _this.playcount = 0;
  270. }
  271. }
  272. }, 50);
  273. },
  274. onTimeupdateTime(res, playFlag) {
  275. if (!res) return;
  276. let audioId = this.audioId;
  277. this.$refs[audioId].currentTime = res;
  278. let time = res - this.bg / 1000;
  279. this.playValue = (time / this.audio.maxTime) * 100;
  280. if (playFlag) {
  281. let audio = document.getElementsByTagName("audio");
  282. audio.forEach((item) => {
  283. if (item.id !== audioId) {
  284. item.pause();
  285. }
  286. });
  287. this.$refs[audioId].play();
  288. }
  289. },
  290. // 将整数转换成 时:分:秒的格式
  291. realFormatSecond(value) {
  292. let theTime = parseInt(value); // 秒
  293. let theTime1 = 0; // 分
  294. let theTime2 = 0; // 小时
  295. if (theTime > 60) {
  296. theTime1 = parseInt(theTime / 60);
  297. theTime = parseInt(theTime % 60);
  298. if (theTime1 > 60) {
  299. theTime2 = parseInt(theTime1 / 60);
  300. theTime1 = parseInt(theTime1 % 60);
  301. }
  302. }
  303. let result = String(parseInt(theTime));
  304. if (result < 10) {
  305. result = "0" + result;
  306. }
  307. if (theTime1 > 0) {
  308. result = String(parseInt(theTime1)) + ":" + result;
  309. if (theTime1 < 10) {
  310. result = "0" + result;
  311. }
  312. } else {
  313. result = "00:" + result;
  314. }
  315. if (theTime2 > 0) {
  316. result = String(parseInt(theTime2)) + ":" + result;
  317. if (theTime2 < 10) {
  318. result = "0" + result;
  319. }
  320. } else {
  321. // result = "00:" + result;
  322. }
  323. return result;
  324. },
  325. },
  326. // 生命周期-创建之前
  327. beforeCreated() {},
  328. // 生命周期-更新之前
  329. beforUpdate() {},
  330. // 生命周期-销毁之前
  331. beforeDestory() {},
  332. // 生命周期-销毁完成
  333. destoryed() {},
  334. };
  335. </script>
  336. <style lang="scss" scoped>
  337. /* @import url(); 引入css类 */
  338. .AudioFull {
  339. .audioLine {
  340. display: flex;
  341. align-items: center;
  342. width: 100%;
  343. height: 40px;
  344. background: #ffffff;
  345. // border: 1px solid rgba(0, 0, 0, 0.1);
  346. // box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
  347. box-sizing: border-box;
  348. border-radius: 4px;
  349. &-green {
  350. background: 0 0;
  351. }
  352. .play {
  353. margin-right: 12px;
  354. margin-left: 8px;
  355. width: 16px;
  356. min-width: 16px;
  357. height: 16px;
  358. cursor: pointer;
  359. display: block;
  360. }
  361. span {
  362. font-size: 16px;
  363. line-height: 19px;
  364. color: #000;
  365. margin-left: 8px;
  366. margin-right: 12px;
  367. font-weight: bold;
  368. font-size: 16px;
  369. line-height: 24px;
  370. text-align: right;
  371. &.color-white {
  372. color: #fff;
  373. }
  374. }
  375. }
  376. .audioLine2 {
  377. .play-icon {
  378. width: 16px;
  379. height: 16px;
  380. cursor: pointer;
  381. &.playBtn-icon {
  382. background: url("../../../assets/icon/pauseC-16-normal-red.png")
  383. no-repeat left top;
  384. background-size: 100% 100%;
  385. }
  386. &.pauseBtn-icon {
  387. background: url("../../../assets/NPC/compare-pause-red.png") no-repeat
  388. left top;
  389. background-size: 100% 100%;
  390. }
  391. }
  392. }
  393. .loadBtn {
  394. background: url("../../../assets/NPC/loading-red.png") no-repeat left top;
  395. background-size: 100% 100%;
  396. }
  397. .audioLine3 {
  398. width: 56px;
  399. height: 56px;
  400. background: #ffffff;
  401. border: 1px solid rgba(0, 0, 0, 0.1);
  402. border-radius: 40px;
  403. display: flex;
  404. justify-content: center;
  405. align-items: center;
  406. .play {
  407. width: 24px;
  408. height: 24px;
  409. cursor: pointer;
  410. &.playBtn {
  411. background: url("../../../assets/icon/pause-24-normal-red.png")
  412. no-repeat left top;
  413. background-size: 100% 100%;
  414. }
  415. &.pauseBtn {
  416. background: url("../../../assets/icon/play-24-normal-red.png") no-repeat
  417. left top;
  418. background-size: 100% 100%;
  419. }
  420. }
  421. &-green {
  422. background: rgba(255, 255, 255, 0.1);
  423. border: 1px solid rgba(0, 0, 0, 0.1);
  424. .play {
  425. &.playBtn {
  426. background: url("../../../assets/icon/pause-24-normal-yellow.png")
  427. no-repeat left top;
  428. background-size: 100% 100%;
  429. }
  430. &.pauseBtn {
  431. background: url("../../../assets/icon/play-24-normal-yellow.png")
  432. no-repeat left top;
  433. background-size: 100% 100%;
  434. }
  435. }
  436. }
  437. }
  438. }
  439. </style>
  440. <style lang="scss">
  441. .AudioFull {
  442. .el-slider__button-wrapper {
  443. position: relative;
  444. z-index: 0;
  445. }
  446. .el-slider__button {
  447. width: 6px;
  448. height: 12px;
  449. border-radius: 6px;
  450. top: 12px;
  451. position: absolute;
  452. }
  453. .el-slider__runway {
  454. margin: 0;
  455. padding: 0;
  456. background: #e5e5e5;
  457. border-radius: 6px;
  458. height: 6px;
  459. }
  460. .el-slider {
  461. position: relative;
  462. }
  463. .el-slider__bar {
  464. height: 6px;
  465. border-radius: 6px 0 0 6px;
  466. background: #de4444;
  467. }
  468. .el-slider__button {
  469. background: #de4444;
  470. border: none;
  471. }
  472. .el-slider__button-wrapper {
  473. width: 25px;
  474. }
  475. .audioLine-green {
  476. .el-slider__bar {
  477. background: #ffc600 !important;
  478. }
  479. .el-slider__button {
  480. background: #ffc600 !important;
  481. }
  482. }
  483. }
  484. .NPC-Book-Sty {
  485. .AudioFull {
  486. .el-slider__bar {
  487. height: 6px;
  488. border-radius: 6px;
  489. background: #de4444;
  490. }
  491. .el-slider__button {
  492. background: #de4444;
  493. border: none;
  494. }
  495. .audioLine-green {
  496. .el-slider__bar {
  497. background: #ffc600 !important;
  498. }
  499. .el-slider__button {
  500. background: #ffc600 !important;
  501. }
  502. }
  503. }
  504. }
  505. .NPC-Big-Book-preview-green {
  506. .AudioFull {
  507. .el-slider__bar {
  508. background: #24b99e !important;
  509. }
  510. .el-slider__button {
  511. background: #24b99e !important;
  512. }
  513. .audioLine-green {
  514. .el-slider__bar {
  515. background: #ffc600 !important;
  516. }
  517. .el-slider__button {
  518. background: #ffc600 !important;
  519. }
  520. }
  521. .audioLine2 {
  522. .play-icon {
  523. &.playBtn-icon {
  524. background: url("../../../assets/icon/pauseC-16-normal-Green.png")
  525. no-repeat left top;
  526. background-size: 100% 100%;
  527. }
  528. &.pauseBtn-icon {
  529. background: url("../../../assets/NPC/compare-pause-green.png")
  530. no-repeat left top;
  531. background-size: 100% 100%;
  532. }
  533. }
  534. }
  535. .loadBtn {
  536. background: url("../../../assets/NPC/loading-green.png") no-repeat left
  537. top;
  538. background-size: 100% 100%;
  539. }
  540. .audioLine3 {
  541. .play {
  542. &.playBtn {
  543. background: url("../../../assets/icon/pause-24-normal-green.png")
  544. no-repeat left top;
  545. background-size: 100% 100%;
  546. }
  547. &.pauseBtn {
  548. background: url("../../../assets/icon/play-24-normal-Green.png")
  549. no-repeat left top;
  550. background-size: 100% 100%;
  551. }
  552. }
  553. &-green {
  554. background: rgba(255, 255, 255, 0.1);
  555. border: 1px solid rgba(0, 0, 0, 0.1);
  556. .play {
  557. &.playBtn {
  558. background: url("../../../assets/icon/pause-24-normal-yellow.png")
  559. no-repeat left top;
  560. background-size: 100% 100%;
  561. }
  562. &.pauseBtn {
  563. background: url("../../../assets/icon/play-24-normal-yellow.png")
  564. no-repeat left top;
  565. background-size: 100% 100%;
  566. }
  567. }
  568. }
  569. }
  570. }
  571. }
  572. .NPC-Big-Book-preview-brown {
  573. .AudioFull {
  574. .el-slider__bar {
  575. background: #bd8865 !important;
  576. }
  577. .el-slider__button {
  578. background: #bd8865 !important;
  579. }
  580. .audioLine-green {
  581. .el-slider__bar {
  582. background: #ffc600 !important;
  583. }
  584. .el-slider__button {
  585. background: #ffc600 !important;
  586. }
  587. }
  588. .audioLine2 {
  589. .play-icon {
  590. &.playBtn-icon {
  591. background: url("../../../assets/icon/pauseC-16-normal-Brown.png")
  592. no-repeat left top;
  593. background-size: 100% 100%;
  594. }
  595. &.pauseBtn-icon {
  596. background: url("../../../assets/NPC/compare-pause-brown.png")
  597. no-repeat left top;
  598. background-size: 100% 100%;
  599. }
  600. }
  601. }
  602. .loadBtn {
  603. background: url("../../../assets/NPC/loading-brown.png") no-repeat left
  604. top;
  605. background-size: 100% 100%;
  606. }
  607. .audioLine3 {
  608. .play {
  609. &.playBtn {
  610. background: url("../../../assets/icon/pause-24-normal-brown.png")
  611. no-repeat left top;
  612. background-size: 100% 100%;
  613. }
  614. &.pauseBtn {
  615. background: url("../../../assets/icon/play-24-normal-Brown.png")
  616. no-repeat left top;
  617. background-size: 100% 100%;
  618. }
  619. }
  620. &-green {
  621. background: rgba(255, 255, 255, 0.1);
  622. border: 1px solid rgba(0, 0, 0, 0.1);
  623. .play {
  624. &.playBtn {
  625. background: url("../../../assets/icon/pause-24-normal-yellow.png")
  626. no-repeat left top;
  627. background-size: 100% 100%;
  628. }
  629. &.pauseBtn {
  630. background: url("../../../assets/icon/play-24-normal-yellow.png")
  631. no-repeat left top;
  632. background-size: 100% 100%;
  633. }
  634. }
  635. }
  636. }
  637. }
  638. }
  639. </style>