AudioLine.vue 8.6 KB

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