记录一次Vue3中自定义audio标签实现拖拽滑动进度和音量的控制


效果

能播放暂停,这个进度条是借助了 ant design 的Slider 滑动输入条,自己也实现了一个 横向滑动进度条,点击或者拖动都可以改变进度。样式可自行修改。

在这里插入图片描述

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。

一、实现思路

写过原生音乐播放器的大概都能知道需要用到audio的那些属性,主要是在界面上将一个audio 隐藏然后通过js来控制音乐的一些操作
template部分

<audio controls ref="audio" style="display: none">
    <source src="../assets/music.mp3"/>
  </audio>

  <div class="container">
    <div class="control">
      <div>
        <img :src="bf" v-if="dataVal.isListen" @click="changePause"/> <img :src="zt" v-else @click="changePause"/>
      </div>
      <div style="height: 200px;display: flex;flex-direction: column;align-items: center">
        <div>{{ dataVal.voice }}%</div>
        <a-slider vertical v-model:value="dataVal.voice" :tip-formatter="null" @change="voiceChange"/>
        <img :src="jingyin_icon" v-if="dataVal.voice === 0" @click="changeVoiceBtn"/>
        <img :src="yinliang_icon" v-else @click="changeVoiceBtn"/>
      </div>
    </div>
    <a-slider v-model:value="percentage" :tip-formatter="null" @change="progressChange"/>
    <div>{{ (dataVal.total * percentage / 100).toFixed(2) }}s/{{ dataVal.total }}s</div>
  </div>

二、横向进度条部分

封装成了一个组件,可变参数如下

const props = defineProps({
  // 背景色
  bgColor: {
    type: String,
    default: '#f0f0f0'
  },
  // 前景色
  activeColor: {
    type: String,
    default: '#4caf50'
  },
  //当前进度
  percentage: {
    type: Number,
    default: 0
  }
})

三、横向进度条组件完整代码

<template>
  <div class="progress-bar" @click="clickProgress" ref="progressBar" :style="{background:props.bgColor}">
    <div class="progress" :style="{ width: progressWidth,background:props.activeColor }"></div>
    <div class="progress-handle" @mousedown="startDrag" :style="{ background:props.activeColor }">{{ progress }}</div>
  </div>
</template>

<script setup>
import {ref, computed, watch, getCurrentInstance} from 'vue';

const {proxy} = getCurrentInstance()
const props = defineProps({
  // 背景色
  bgColor: {
    type: String,
    default: '#f0f0f0'
  },
  // 前景色
  activeColor: {
    type: String,
    default: '#4caf50'
  },
  //当前进度
  percentage: {
    type: Number,
    default: 0
  }
})

const progress = ref(0);
const progressBar = ref(null);
let dragging = false;
let dragStartX = 0;


watch(
    () => [progress, props.percentage],
    ([progress, percentage]) => {
      if (percentage) {
        // progress.value = percentage.toFixed(2)
      }
    },
    {deep: true}
)

const updateProgress = (clientX) => {
  const progressBarRect = progressBar.value.getBoundingClientRect();
  const progressWidthPercentage = ((clientX - progressBarRect.left) / progressBarRect.width) * 100;
  progress.value = Math.max(0, Math.min(progressWidthPercentage, 100)).toFixed(2);
  proxy.$emit('valueChange', progress.value)
};

const startDrag = (event) => {
  dragging = true;
  dragStartX = event.clientX;
  document.addEventListener('mousemove', handleDrag);
  document.addEventListener('mouseup', endDrag);
};

const handleDrag = (event) => {
  if (dragging) {
    updateProgress(event.clientX);
  }
};

const endDrag = () => {
  dragging = false;
  document.removeEventListener('mousemove', handleDrag);
  document.removeEventListener('mouseup', endDrag);
};

const clickProgress = (event) => {
  updateProgress(event.clientX);
};

const progressWidth = computed(() => {
  return progress.value + '%';
});

</script>

<style scoped>
.progress-bar {
  width: 99%;
  height: 20px;
  border-radius: 10px;
  display: flex;
  justify-content: start;
  position: relative;
  cursor: pointer;
}

.progress {
  height: 100%;
  border-radius: 10px;
  transition: width 0.2s;
}

.progress-handle {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-top: -10px;
  margin-left: -10px;
  line-height: 40px;
  color: #fff;
  text-align: center;
}
</style>

四、自定义audio完整代码

<script setup>
import bf from '../assets/bf.png'
import zt from '../assets/zt.png'
import jingyin_icon from '../assets/jingyin_icon.png'
import yinliang_icon from '../assets/yinliang_icon.png'
import Progress from './progress.vue'
import {reactive, ref, onMounted, getCurrentInstance, onActivated, watch} from 'vue'

const dataVal = reactive({
  isListen: false,
  positionY: 0,
  voice: 100,
  total: 0.00,
})

const audio = ref()
const percentage = ref(0)


//播放还是暂停

let timer = null

const changePause = () => {
  if (dataVal.isListen) {
    audio.value.pause(); //暂停
  } else {
    audio.value.play(); //播放
    if (timer) {
      clearTimeout(timer)
    } else {
      setInterval(() => {
        percentage.value = (audio.value.currentTime / audio.value.duration * 100).toFixed(2)
      }, 100)
    }
  }
  dataVal.isListen = !dataVal.isListen
  dataVal.total = audio.value.duration.toFixed(2) //总时间
}
const progressChange = (val) => {
  dataVal.total = audio.value.duration.toFixed(2) //总时间
  audio.value.currentTime = audio.value.duration * val / 100
  audio.value.play(); //播放.
  dataVal.isListen = true
}
const voiceChange = (val) => {
  audio.value.volume = val / 100
}
const changeVoiceBtn = () => {
  dataVal.voice = dataVal.voice > 0 ? 0 : 100
  audio.value.volume = dataVal.voice / 100
}
</script>

<template>
  <audio controls ref="audio" style="display: none">
    <source src="../assets/music.mp3"/>
  </audio>

  <div class="container">
    <div class="control">
      <div>
        <img :src="bf" v-if="dataVal.isListen" @click="changePause"/> <img :src="zt" v-else @click="changePause"/>
      </div>
      <div style="height: 200px;display: flex;flex-direction: column;align-items: center">
        <div>{{ dataVal.voice }}%</div>
        <a-slider vertical v-model:value="dataVal.voice" :tip-formatter="null" @change="voiceChange"/>
        <img :src="jingyin_icon" v-if="dataVal.voice === 0" @click="changeVoiceBtn"/>
        <img :src="yinliang_icon" v-else @click="changeVoiceBtn"/>
      </div>
    </div>
    <a-slider v-model:value="percentage" :tip-formatter="null" @change="progressChange"/>
    <div>{{ (dataVal.total * percentage / 100).toFixed(2) }}s/{{ dataVal.total }}s</div>
  </div>
  <!--<Progress style="margin-top: 100px" @valueChange="progressChange" :percentage="percentage" activeColor="#3F50F2" bgColor="#77737d"/>-->
</template>

<style scoped>
.container {
  background: pink;
  width: 80%;
  height: 400px;
  margin: 0 auto;
  padding: 30px;
}

.control {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  height: 80%;
}
</style>

项目地址

https://github.com/codernmx/audio

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codernmx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值