音乐播放功能实现 - 2

这次我们来继续介绍音量控制和进度条的实现

音量控制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// useUserStore

//当前播放器的音量
    const volume = ref<number>(0.5)
    
    
// volume.vue
   <template>
    <div class="volume-box w-[150px] flex items-center">
      <i v-if="volume !== 0" class="iconfont icon-yinliang" @click="volumeHandler(true)" />
      <i v-else class="iconfont icon-jingyin" @click="volumeHandler(false)" />
      <el-slider v-model="model" :show-tooltip="false" @input="input" @change="change"
        style="width: 80px; overflow: hidden" />
      <div class="volume-value text-[12px] ml-[10px]">
        {{ model }}%
      </div>
    </div>
  </template>


  <script setup lang="ts">
  import { computed, ref, watch } from 'vue'
  import { useUserStore } from '@/store'
  import { userAudio } from '@/components/MusicPlayer/index.vue'

  interface Props {
    audio?: userAudio
  }

  const props = defineProps<Props>()
  const userStore = useUserStore()

  /** slider 的百分比值 */
  const model = ref(0)

  /** 真实音量 0 ~ 1 */
  const volume = computed(() => model.value / 100)



  /** 初始化 / audio 就绪时同步音量 */
  watch(
    () => props.audio,
    (audio) => {
     if (!audio) return
      const saved = userStore.volume
      const initVolume = Number.isNaN(saved) ? 1 : saved

      model.value = initVolume * 100
      audio.volume = initVolume
 
    },
    { immediate: true }
  )

  
  </script>

我们将音量先进行持久化存储,之后对父组件接收传来的audio实例进行监听并将值赋给它

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/** 统一设置音量的方法(核心) */
const setVolume = (v: number) => {
  if (!props.audio) return

  props.audio.volume = v
  userStore.volume = v
}
// 静音前的音量(0 ~ 1)
const beforeVolume = ref(1)

const volumeHandler = (mute: boolean) => {

  if (mute && volume.value > 0) {
    beforeVolume.value = volume.value
  }

  const target = mute
    ? 0
    : beforeVolume.value || 1

  model.value = target * 100
  setVolume(target)
}
/** 拖动 slider */
const input = () => {
  setVolume(volume.value)
}

/** 松手后同步到 store */
const change = () => {
  userStore.volume = volume.value
}

setVolume的函数 接收到传来的将值赋给audio和存到store,静音的话我们这里处理一下,在点击静音前我们将之前一次的音量存起来,这样从静音到点到非静音时可以恢复

进度条功能实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// ProgressBar.vue
<template>
  <div class="base-progress-bar w-full" v-if="props.songs?.ar">
    <el-slider
      :class="{ 'is-dragging': isSeeking }"
      v-model="slider"
      :show-tooltip="false"
      @input="onStart"
      @change="onEnd"

    />
  </div>
</template>

<script setup lang="ts">
import { GetMusicDetailData } from '@/types/musicList'
import { useMusicStore } from '@/store'
import { computed, ref } from 'vue'

interface Props {
  songs?: GetMusicDetailData
}
const props = defineProps<Props>()
const musicStore = useMusicStore()

/** 是否正在拖动 */
const isSeeking = ref(false)
/** 拖动时的临时时间 */
const tempTime = ref(0)

/** 歌曲总时长(秒) */
const duration = computed(() => {
  return (musicStore.songs?.dt ?? 0) / 1000
})

/** slider 百分比 */
const slider = computed<number>({
  get() {
    const time =  musicStore.currentTime

    return duration.value
      ? (time / duration.value) * 100
      : 0
  },
  set(val) {
    const time = (val * duration.value) / 100
    tempTime.value = time

  }
})

const onStart = () => {
  isSeeking.value = true

}

const onEnd = () => {

  isSeeking.value = false

  // 松手才真正设置 audio
  window.$audio.time = tempTime.value
  musicStore.currentTime = tempTime.value
}
</script>

这里的处理可以看一下在拖动的时候这里并没有直接给audio.current直接赋值而是在end的时候再进行赋值处理

window.$audio.time = tempTime.value这里的可以回顾一下我们第一章介绍的

audio.currentTime是双精度浮点型,所以musicStore里面的currentTime和我们设置的都必须是以秒为单位的,这里就可以理解我这里获取duration和在set里面Time的处理了

使用 Hugo 构建
主题 StackJimmy 设计