AudioLine.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <template>
  2. <div class="Audio">
  3. <div class="audioLine" v-if="!hideSlider">
  4. <div
  5. class="play"
  6. :class="audio.playing ? 'playBtn' : 'pauseBtn'"
  7. @click="PlayAudio"
  8. />
  9. <template v-if="!isRepeat">
  10. <el-slider
  11. v-model="playValue"
  12. :style="{ width: sliderWidth + 'px', height: '2px' }"
  13. :format-tooltip="formatProcessToolTip"
  14. @change="changeCurrentTime"
  15. />
  16. <span><template v-if="audio.playing">-</template>{{
  17. audio.maxTime
  18. ? realFormatSecond(audio.maxTime - audio.currentTime)
  19. : ""
  20. }}</span>
  21. </template>
  22. <audio
  23. ref="audio"
  24. :src="mp3"
  25. @loadedmetadata="onLoadedmetadata"
  26. @timeupdate="onTimeupdate"
  27. />
  28. </div>
  29. <div class="audioLine" v-else>
  30. <div
  31. class="play"
  32. :class="audio.playing ? 'playBtn' : 'pauseBtn'"
  33. @click="PlayAudio"
  34. />
  35. </div>
  36. <audio
  37. :ref="audioId"
  38. :src="mp3"
  39. @loadedmetadata="onLoadedmetadata"
  40. @timeupdate="onTimeupdate"
  41. :id="audioId"
  42. />
  43. </div>
  44. </template>
  45. <script>
  46. // 这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  47. // 例如:import 《组件名称》from ‘《组件路径》';
  48. export default {
  49. // import引入的组件需要注入到对象中才能使用
  50. components: {},
  51. props: [
  52. "mp3",
  53. "getCurTime",
  54. "stopAudio",
  55. "width",
  56. "isRepeat",
  57. "themeColor",
  58. "hideSlider",
  59. "ed",
  60. "bg",
  61. "audioId",
  62. ],
  63. data() {
  64. // 这里存放数据
  65. return {
  66. playValue: 0,
  67. audio: {
  68. // 该字段是音频是否处于播放状态的属性
  69. playing: false,
  70. // 音频当前播放时长
  71. currentTime: 0,
  72. // 音频最大播放时长
  73. maxTime: 0,
  74. isPlaying: false,
  75. },
  76. audioAllTime: null, // 展示总时间
  77. duioCurrentTime: null, // 剩余时间
  78. };
  79. },
  80. // 计算属性 类似于data概念
  81. computed: {
  82. sliderWidth() {
  83. let width = 0;
  84. if (this.width) {
  85. width = this.width;
  86. } else {
  87. width = 662;
  88. }
  89. return width;
  90. },
  91. },
  92. // 监控data中数据变化
  93. watch: {
  94. stopAudio: {
  95. handler(val, oldVal) {
  96. const _this = this;
  97. if (val) {
  98. _this.$refs[_this.audioId].pause();
  99. _this.audio.playing = false;
  100. }
  101. },
  102. // 深度观察监听
  103. deep: true,
  104. },
  105. "audio.playing": {
  106. handler(val) {
  107. this.$emit("playChange", val);
  108. if (val) this.$emit("handleChangeStopAudio");
  109. },
  110. },
  111. },
  112. // 生命周期 - 创建完成(可以访问当前this实例)
  113. created() {},
  114. // 生命周期 - 挂载完成(可以访问DOM元素)
  115. mounted() {
  116. let _this = this;
  117. let audioId = _this.audioId;
  118. _this.$refs[audioId].addEventListener("play", function () {
  119. _this.audio.playing = true;
  120. _this.audio.isPlaying = true;
  121. });
  122. _this.$refs[audioId].addEventListener("pause", function () {
  123. _this.audio.playing = false;
  124. });
  125. _this.$refs[audioId].addEventListener("ended", function () {
  126. _this.audio.playing = false;
  127. _this.audio.isPlaying = false;
  128. });
  129. this.$nextTick(() => {
  130. document
  131. .getElementsByClassName("el-slider__button-wrapper")[0]
  132. .addEventListener("mousedown", function () {
  133. _this.$refs[audioId].pause();
  134. _this.audio.playing = false;
  135. });
  136. });
  137. },
  138. // 生命周期-挂载之前
  139. beforeMount() {},
  140. // 生命周期-更新之后
  141. updated() {},
  142. // 如果页面有keep-alive缓存功能,这个函数会触发
  143. activated() {},
  144. // 方法集合
  145. methods: {
  146. PlayAudio() {
  147. let audioId = this.audioId;
  148. let audio = document.getElementsByTagName("audio");
  149. audio.forEach((item) => {
  150. if (item.id !== audioId) {
  151. item.pause();
  152. }
  153. });
  154. if (this.audio.playing) {
  155. this.$refs[audioId].pause();
  156. this.audio.playing = false;
  157. this.$emit("handleListenRead", false);
  158. } else {
  159. if (this.hideSlider) {
  160. this.$refs[audioId].play();
  161. this.onTimeupdateTime(this.bg / 1000);
  162. } else {
  163. this.$refs[audioId].play();
  164. }
  165. this.audio.playing = true;
  166. this.$emit("handleChangeStopAudio");
  167. this.$emit("handleListenRead", true);
  168. }
  169. },
  170. // 点击 拖拽播放音频
  171. changeCurrentTime(value) {
  172. let audioId = this.audioId;
  173. this.$refs[audioId].play();
  174. this.audio.playing = true;
  175. this.$refs[audioId].currentTime = parseInt(
  176. (value / 100) * this.audio.maxTime
  177. );
  178. },
  179. mousedown() {
  180. let audioId = this.audioId;
  181. this.$refs[audioId].pause();
  182. this.audio.playing = false;
  183. },
  184. // 进度条格式化toolTip
  185. formatProcessToolTip(index) {
  186. index = parseInt((this.audio.maxTime / 100) * index);
  187. return this.realFormatSecond(index);
  188. },
  189. // 音频加载完之后
  190. onLoadedmetadata(res) {
  191. this.audio.maxTime = parseInt(res.target.duration);
  192. this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
  193. },
  194. // 当音频当前时间改变后,进度条也要改变
  195. onTimeupdate(res) {
  196. let audioId = this.audioId;
  197. this.audio.currentTime = res.target.currentTime;
  198. this.getCurTime(res.target.currentTime);
  199. this.playValue = (this.audio.currentTime / this.audio.maxTime) * 100;
  200. if (this.audio.currentTime * 1000 > this.ed) {
  201. this.$refs[audioId].pause();
  202. }
  203. },
  204. onTimeupdateTime(res, playFlag) {
  205. let audioId = this.audioId;
  206. this.$refs[audioId].currentTime = res;
  207. this.playValue = (res / this.audio.maxTime) * 100;
  208. if (playFlag) {
  209. let audio = document.getElementsByTagName("audio");
  210. audio.forEach((item) => {
  211. if (item.id !== audioId) {
  212. item.pause();
  213. }
  214. });
  215. this.$refs[audioId].play();
  216. }
  217. },
  218. // 将整数转换成 时:分:秒的格式
  219. realFormatSecond(value) {
  220. let theTime = parseInt(value); // 秒
  221. let theTime1 = 0; // 分
  222. let theTime2 = 0; // 小时
  223. if (theTime > 60) {
  224. theTime1 = parseInt(theTime / 60);
  225. theTime = parseInt(theTime % 60);
  226. if (theTime1 > 60) {
  227. theTime2 = parseInt(theTime1 / 60);
  228. theTime1 = parseInt(theTime1 % 60);
  229. }
  230. }
  231. let result = String(parseInt(theTime));
  232. if (result < 10) {
  233. result = "0" + result;
  234. }
  235. if (theTime1 > 0) {
  236. result = String(parseInt(theTime1)) + ":" + result;
  237. if (theTime1 < 10) {
  238. result = "0" + result;
  239. }
  240. } else {
  241. result = "00:" + result;
  242. }
  243. if (theTime2 > 0) {
  244. result = String(parseInt(theTime2)) + ":" + result;
  245. if (theTime2 < 10) {
  246. result = "0" + result;
  247. }
  248. } else {
  249. // result = "00:" + result;
  250. }
  251. return result;
  252. },
  253. },
  254. // 生命周期-创建之前
  255. beforeCreated() {},
  256. // 生命周期-更新之前
  257. beforUpdate() {},
  258. // 生命周期-销毁之前
  259. beforeDestory() {},
  260. // 生命周期-销毁完成
  261. destoryed() {},
  262. };
  263. </script>
  264. <style lang="scss" scoped>
  265. /* @import url(); 引入css类 */
  266. .Audio {
  267. width: 100%;
  268. .audioLine {
  269. display: flex;
  270. align-items: center;
  271. width: 100%;
  272. height: 40px;
  273. background: #ffffff;
  274. // border: 1px solid rgba(0, 0, 0, 0.1);
  275. // box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1);
  276. box-sizing: border-box;
  277. border-radius: 4px;
  278. .play {
  279. margin-right: 12px;
  280. margin-left: 8px;
  281. width: 16px;
  282. height: 16px;
  283. cursor: pointer;
  284. display: block;
  285. // &.playBtn {
  286. // background: url("../../../assets/pause.png") no-repeat left top;
  287. // background-size: 100% 100%;
  288. // }
  289. // &.pauseBtn {
  290. // background: url("../../../assets/play.png") no-repeat left top;
  291. // background-size: 100% 100%;
  292. // }
  293. }
  294. span {
  295. font-size: 16px;
  296. line-height: 19px;
  297. color: #000;
  298. margin-left: 8px;
  299. margin-right: 12px;
  300. min-width: 56px;
  301. text-align: right;
  302. }
  303. }
  304. }
  305. </style>
  306. <style lang="scss">
  307. .Audio {
  308. .el-slider__button-wrapper {
  309. position: relative;
  310. z-index: 0;
  311. }
  312. .el-slider__button {
  313. width: 8px;
  314. height: 8px;
  315. top: 12px;
  316. position: absolute;
  317. }
  318. .el-slider__runway {
  319. margin: 0;
  320. padding: 0;
  321. background: #e5e5e5;
  322. border-radius: 0px;
  323. height: 2px;
  324. }
  325. .el-slider {
  326. position: relative;
  327. }
  328. .el-slider__bar {
  329. height: 2px;
  330. background: rgba(118, 99, 236, 1);
  331. }
  332. .el-slider__button {
  333. background: rgba(118, 99, 236, 1);
  334. border: none;
  335. }
  336. }
  337. .NPC-Book-Sty {
  338. .Audio {
  339. .el-slider__bar {
  340. height: 2px;
  341. background: #de4444;
  342. }
  343. .el-slider__button {
  344. background: #de4444;
  345. border: none;
  346. }
  347. }
  348. }
  349. .NPC-Big-Book-preview-green {
  350. .Audio {
  351. .el-slider__bar {
  352. background: #24b99e !important;
  353. }
  354. .el-slider__button {
  355. background: #24b99e !important;
  356. }
  357. }
  358. }
  359. .NPC-Big-Book-preview-brown {
  360. .Audio {
  361. .el-slider__bar {
  362. background: #bd8865 !important;
  363. }
  364. .el-slider__button {
  365. background: #bd8865 !important;
  366. }
  367. }
  368. }
  369. </style>