AI销管
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

378 lines
8.6 KiB

  1. <template>
  2. <view class="imt-audio">
  3. <template>
  4. <view class="top">
  5. <view class="audio-control-wrapper">
  6. <image :src="require('./static/loading.png')" v-if="playState=='loading'" class="play loading">
  7. </image>
  8. <template v-else>
  9. <image :src="require('./static/playbtn.png')" alt="play" @click="play" class="play"
  10. v-if="playState=='pause'"></image>
  11. <image :src="require('./static/pausebtn.png')" alt="pause" @click="pause" class="play" v-else>
  12. </image>
  13. </template>
  14. </view>
  15. </view>
  16. <view class="audio-wrapper">
  17. <view class="audio-flex">
  18. <text>
  19. {{formatSeconds(currentTime)}}
  20. </text>
  21. <slider class="audio-slider" block-size="12" :max="duration" :value="currentTime"
  22. @change="sliderChange" @changing="sliderChanging"></slider>
  23. <text>
  24. {{formatSeconds(duration)}}
  25. </text>
  26. </view>
  27. <view class="slidebox">
  28. <slot name="extraCtrls">
  29. <image @click="backAudio" class="slide-img" :src="require('./static/backimg.png')" mode="">
  30. </image>
  31. <!-- <text class="hItem extrButton" @click="$emit('Button1Click')"
  32. v-show="isButton1Visible">{{button1Text}}</text>
  33. <text class="hItem extrButton" @click="$emit('Button2Click')"
  34. v-show="isButton2Visible">{{button2Text}}</text>
  35. <text class="hItem extrButton" @click="$emit('Button3Click')"
  36. v-show="isButton3Visible">{{button3Text}}</text> -->
  37. </slot>
  38. </view>
  39. </view>
  40. <!--video在ios中不能完全隐藏,否则无法播放-->
  41. <video id="videoPlayer" :autoplay="true" class="videoPlayer" :src="src" :muted="false" style="width: 10rpx;height:10rpx;"
  42. @play="playerOnPlay" @pause="playerOnPause" @ended="playerOnEnded" @timeupdate="playerOnTimeupdate"
  43. @waiting="playerOnWaiting" @error="playerOnError"></video>
  44. </template>
  45. </view>
  46. </template>
  47. <script>
  48. /*
  49. createInnerAudioContext()是audio组件的内部实现,不能熄屏播放、不能后台播放、不能倍速播放。
  50. getBackgroundAudioManager() 可以熄屏播放、后台播放,不能倍速播放。缺点是响应速度很慢,无法实现精细、及时的进度控制,而且可能被别的程序占用。
  51. 因此这里只能用video来实现,video能倍速播放,不能熄屏播放、不能后台播放。而且避免了用createInnerAudioContext()实现的跳转到别的页面,还在播放的问题
  52. 因此应用程序可以在需要后台播放的时候(需要用户操作触发),再暂停这个控件的播放,然后自己用getBackgroundAudioManager实现后台播放
  53. */
  54. import Vue from 'vue';
  55. import {
  56. mapState,
  57. mapActions,
  58. mapMutations
  59. } from 'vuex'
  60. export default {
  61. props: {
  62. isButton1Visible: {
  63. type: Boolean,
  64. default: false
  65. },
  66. button1Text: {
  67. type: String,
  68. default: ''
  69. },
  70. isButton2Visible: {
  71. type: Boolean,
  72. default: false
  73. },
  74. button2Text: {
  75. type: String,
  76. default: ''
  77. },
  78. isButton3Visible: {
  79. type: Boolean,
  80. default: false
  81. },
  82. button3Text: {
  83. type: String,
  84. default: ''
  85. },
  86. nowFileTime: {
  87. type: [String, Number],
  88. default: 0
  89. },
  90. },
  91. watch: {
  92. nowFileTime(oValue, nValue) {
  93. this.duration = nValue
  94. }
  95. },
  96. data() {
  97. return {
  98. src: '', //
  99. poster: "",
  100. name: "...",
  101. singer: "...",
  102. duration: 0,
  103. currentTime: 0,
  104. playState: "pause", //"loading"/"playing"/"pause"
  105. isSliderChanging: false,
  106. isFirst: false, // 是否阻止第一次赋值
  107. audio: null, // 音频对象
  108. };
  109. },
  110. created() {
  111. // 自定义组件,需要传递第二个参数为this,否则后续的pause等操作不起作用
  112. this.videoCtx = uni.createVideoContext("videoPlayer", this);
  113. this.audio = uni.createInnerAudioContext();
  114. this.audio.autoplay = false;
  115. this.createAudio()
  116. },
  117. mounted() {
  118. this.audio.onCanplay((e) => {
  119. if (this.audio.duration != 0) {
  120. this.playState = "pause"
  121. this.$forceUpdate()
  122. }
  123. })
  124. },
  125. methods: {
  126. ...mapMutations(['createAudio', 'setAudio']),
  127. setSrc(value) {
  128. console.log(this, ' 我打印this')
  129. this.src = value;
  130. console.log(this.src, '我在这儿里更换src')
  131. // 获取当前音频的总时长
  132. this.audio.src = value;
  133. },
  134. setPoster(value) {
  135. this.poster = value;
  136. },
  137. setName(value) {
  138. this.name = value;
  139. },
  140. setSinger(value) {
  141. this.singer = value;
  142. },
  143. playerOnPlay(e) {
  144. this.playState = "playing";
  145. console.log('playerOnPlay', e)
  146. this.$emit("play");
  147. },
  148. playerOnPause(e) {
  149. this.playState = "pause";
  150. console.log('playerOnPause', e)
  151. this.$emit("pause");
  152. },
  153. playerOnEnded(e) {
  154. this.playState = "pause";
  155. console.log('playerOnEnded', e)
  156. this.$emit("ended");
  157. },
  158. playerOnTimeupdate(e) {
  159. if (this.isFirst) this.playState = "playing";
  160. this.isFirst = true
  161. this.duration = e.detail.duration;
  162. this.currentTime = e.detail.currentTime;
  163. this.$emit("timeUpdate", e.detail);
  164. },
  165. playerOnWaiting(e) {
  166. this.playState = "loading";
  167. console.log('playerOnWaiting', e)
  168. },
  169. playerOnError(e) {
  170. console.log('playerOnError', e)
  171. this.playState = "pause";
  172. this.$emit("error", e);
  173. },
  174. formatSeconds(seconds) {
  175. var result = typeof seconds === "string" ? parseFloat(seconds) : seconds;
  176. if (isNaN(result)) return "";
  177. let h = Math.floor(result / 3600) < 10 ?
  178. "0" + Math.floor(result / 3600) :
  179. Math.floor(result / 3600);
  180. let m = Math.floor((result / 60) % 60) < 10 ?
  181. "0" + Math.floor((result / 60) % 60) :
  182. Math.floor((result / 60) % 60) + h * 60;
  183. let s = Math.floor(result % 60) < 10 ?
  184. "0" + Math.floor(result % 60) :
  185. Math.floor(result % 60);
  186. return `${h}:${m}:${s}`;
  187. },
  188. stop() {
  189. this.videoCtx.stop();
  190. },
  191. seek(t) {
  192. this.videoCtx.seek(t);
  193. },
  194. play() {
  195. console.log('触发方法play')
  196. this.videoCtx.play(); //在有的H5浏览器里,如果play不是用户触发的,则play()会报错
  197. this.$store.commit('stopAduio')
  198. },
  199. pause() {
  200. console.log('触发方法pause')
  201. this.videoCtx.pause();
  202. },
  203. playbackRate(value) {
  204. this.videoCtx.playbackRate(value);
  205. //playbackRate不能在play之前或者之后立即调用,否则只有很少几率会成功
  206. },
  207. sliderChange(e) {
  208. this.isSliderChanging = false;
  209. //要通过e.detail.value获取,否则如果通过dom去读取slider的value
  210. //就会存在滚动条拖不动的情况
  211. // this.videoCtx.seek(e.detail.value);
  212. this.$emit('sliderChangeComplate', e)
  213. },
  214. sliderChanging(e) {
  215. this.isSliderChanging = true;
  216. console.log(e, '当前正在改变')
  217. },
  218. // 点击后台播放音频事件
  219. backAudio() {
  220. this.pause()
  221. let obj = {
  222. src: this.src,
  223. currentTime: this.currentTime
  224. }
  225. this.setAudio(obj)
  226. },
  227. },
  228. }
  229. </script>
  230. <style lang="scss">
  231. // @import './index.scss';
  232. @mixin textoverflow() {
  233. display: -webkit-box;
  234. overflow: hidden;
  235. text-overflow: ellipsis;
  236. -webkit-box-orient: vertical;
  237. -webkit-line-clamp: 1;
  238. }
  239. @keyframes rowup {
  240. 0% {
  241. -webkit-transform: translate(-50%, -50%) rotate(0deg);
  242. transform-origin: center center;
  243. }
  244. 100% {
  245. -webkit-transform: translate(-50%, -50%) rotate(360deg);
  246. transform-origin: center center;
  247. }
  248. }
  249. .imt-audio {
  250. position: relative;
  251. width: 100%;
  252. height: 81rpx;
  253. display: flex;
  254. box-sizing: border-box;
  255. background: #fff;
  256. overflow: hidden;
  257. .top {
  258. position: relative;
  259. width: 100rpx;
  260. }
  261. .audio-wrapper {
  262. padding: 0 20rpx;
  263. display: flex;
  264. flex: 1;
  265. color: #fff;
  266. }
  267. .slidebox {
  268. flex-shrink: 0;
  269. display: flex;
  270. .slide-img {
  271. width: 75rpx;
  272. height: 75rpx;
  273. }
  274. }
  275. /deep/ .uni-slider-tap-area {
  276. padding: 0;
  277. }
  278. /deep/ .uni-slider-wrapper {
  279. min-height: 0;
  280. }
  281. /deep/ .uni-slider-handle-wrapper {
  282. height: 6px;
  283. }
  284. .audio-slider {
  285. flex-grow: 1;
  286. }
  287. .play {
  288. width: 48rpx;
  289. height: 48rpx;
  290. z-index: 99;
  291. background: rgba(0, 0, 0, 0.4);
  292. border-radius: 50%;
  293. position: absolute;
  294. top: 50%;
  295. left: 50%;
  296. transform: translate(-50%, -50%);
  297. &.loading {
  298. width: 48rpx;
  299. height: 48rpx;
  300. animation: rotating_theme3 2s linear infinite;
  301. }
  302. }
  303. }
  304. .audio-flex {
  305. padding: 0 32rpx 0 0;
  306. flex-grow: 1;
  307. display: flex;
  308. align-items: center;
  309. text {
  310. color: #70798D;
  311. }
  312. }
  313. @keyframes rotating {
  314. 0% {
  315. transform: rotateZ(0deg)
  316. }
  317. 100% {
  318. transform: rotateZ(360deg)
  319. }
  320. }
  321. @keyframes rotating_theme3 {
  322. 0% {
  323. transform: translate(-50%, -50%) rotateZ(0deg)
  324. }
  325. 100% {
  326. transform: translate(-50%, -50%) rotateZ(360deg)
  327. }
  328. }
  329. .hItem {
  330. margin-left: 16rpx;
  331. }
  332. .extrButton {
  333. font-size: 36rpx;
  334. }
  335. .videoPlayer {
  336. position: absolute;
  337. left: 0;
  338. bottom: 0;
  339. z-index: -1;
  340. }
  341. </style>