video组件封装

<template>
  <div class="video-container" :style="containerStyle">
    <video
      ref="videoRef"
      class="video-player"
      :width="width"
      :height="height"
      :poster="poster"
      :autoplay="autoplay"
      :loop="loop"
      :muted="muted"
      :controls="showControls"
      :preload="preload"
      @play="onPlay"
      @pause="onPause"
      @ended="onEnded"
      @timeupdate="onTimeUpdate"
      @volumechange="onVolumeChange"
      @error="onError"
    >
      <source v-for="source in sources" :key="source.src" :src="source.src" :type="source.type" />
      您的浏览器不支持 HTML5 视频标签。
    </video>
    
    <!-- 自定义控制条(可选) -->
    <div v-if="customControls" class="custom-controls">
      <button @click="togglePlay">
        {{ isPlaying ? '暂停' : '播放' }}
      </button>
      <input
        type="range"
        min="0"
        :max="duration"
        v-model="currentTime"
        @input="seek"
      />
      <button @click="toggleMute">
        {{ isMuted ? '取消静音' : '静音' }}
      </button>
      <input
        type="range"
        min="0"
        max="1"
        step="0.01"
        v-model="volume"
        @input="changeVolume"
      />
    </div>
  </div>
</template>

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

const props = defineProps({
  // 视频源,支持多个源格式
  sources: {
    type: Array,
    required: true,
    validator: (value) => {
      return value.every(source => source.src && source.type)
    }
  },
  // 视频宽度
  width: {
    type: [String, Number],
    default: '100%'
  },
  // 视频高度
  height: {
    type: [String, Number],
    default: 'auto'
  },
  // 封面图
  poster: {
    type: String,
    default: ''
  },
  // 是否自动播放
  autoplay: {
    type: Boolean,
    default: false
  },
  // 是否循环播放
  loop: {
    type: Boolean,
    default: false
  },
  // 是否静音
  muted: {
    type: Boolean,
    default: false
  },
  // 是否显示原生控制条
  showControls: {
    type: Boolean,
    default: true
  },
  // 预加载方式
  preload: {
    type: String,
    default: 'auto',
    validator: (value) => ['auto', 'metadata', 'none'].includes(value)
  },
  // 是否使用自定义控制条
  customControls: {
    type: Boolean,
    default: false
  },
  // 容器样式
  containerStyle: {
    type: Object,
    default: () => ({})
  }
})

const emit = defineEmits([
  'play',
  'pause',
  'ended',
  'timeupdate',
  'volumechange',
  'error',
  'ready'
])

const videoRef = ref(null)
const isPlaying = ref(false)
const isMuted = ref(props.muted)
const duration = ref(0)
const currentTime = ref(0)
const volume = ref(props.muted ? 0 : 1)

// 初始化视频
onMounted(() => {
  if (videoRef.value) {
    // 确保muted属性与props同步
    videoRef.value.muted = props.muted
    isMuted.value = props.muted
    
    // 获取视频时长
    const updateDuration = () => {
      duration.value = videoRef.value.duration || 0
    }
    
    videoRef.value.addEventListener('loadedmetadata', updateDuration)
    videoRef.value.addEventListener('durationchange', updateDuration)
    
    emit('ready', videoRef.value)
  }
})

// 播放/暂停切换
const togglePlay = () => {
  if (!videoRef.value) return
  
  if (isPlaying.value) {
    videoRef.value.pause()
  } else {
    videoRef.value.play()
  }
}

// 跳转到指定时间
const seek = () => {
  if (videoRef.value) {
    videoRef.value.currentTime = currentTime.value
  }
}

// 音量调整
const changeVolume = () => {
  if (videoRef.value) {
    videoRef.value.volume = volume.value
    isMuted.value = volume.value === 0
  }
}

// 静音切换
const toggleMute = () => {
  if (videoRef.value) {
    videoRef.value.muted = !isMuted.value
    isMuted.value = !isMuted.value
    if (!isMuted.value) {
      volume.value = videoRef.value.volume
    }
  }
}

// 事件处理
const onPlay = () => {
  isPlaying.value = true
  emit('play')
}

const onPause = () => {
  isPlaying.value = false
  emit('pause')
}

const onEnded = () => {
  isPlaying.value = false
  emit('ended')
}

const onTimeUpdate = () => {
  if (videoRef.value) {
    currentTime.value = videoRef.value.currentTime
    emit('timeupdate', currentTime.value)
  }
}

const onVolumeChange = () => {
  if (videoRef.value) {
    volume.value = videoRef.value.volume
    isMuted.value = videoRef.value.muted
    emit('volumechange', {
      volume: volume.value,
      muted: isMuted.value
    })
  }
}

const onError = () => {
  if (videoRef.value) {
    emit('error', videoRef.value.error)
  }
}

// 暴露方法给父组件
defineExpose({
  play: () => videoRef.value?.play(),
  pause: () => videoRef.value?.pause(),
  togglePlay,
  toggleMute,
  seek,
  setVolume: (val) => {
    if (videoRef.value) {
      videoRef.value.volume = val
      volume.value = val
    }
  },
  getVideoElement: () => videoRef.value
})
</script>

<style scoped>
.video-container {
  position: relative;
  display: inline-block;
  line-height: 0;
}

.video-player {
  max-width: 100%;
  height: auto;
  display: block;
  background-color: #000;
}

.custom-controls {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.5);
  padding: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.custom-controls button {
  background: #fff;
  border: none;
  padding: 5px 10px;
  border-radius: 3px;
  cursor: pointer;
}

.custom-controls input[type="range"] {
  flex-grow: 1;
}
</style>

使用

<template>
  <div>
    <VideoPlayer
      :sources="[
        { src: 'https://example.com/video.mp4', type: 'video/mp4' },
        { src: 'https://example.com/video.webm', type: 'video/webm' }
      ]"
      poster="https://example.com/poster.jpg"
      :autoplay="false"
      :loop="false"
      :customControls="true"
      @play="handlePlay"
      @pause="handlePause"
      ref="videoPlayer"
    />
    
    <button @click="$refs.videoPlayer.togglePlay()">外部控制播放/暂停</button>
  </div>
</template>

<script setup>
import VideoPlayer from './components/VideoPlayer.vue'

const handlePlay = () => {
  console.log('视频开始播放')
}

const handlePause = () => {
  console.log('视频暂停')
}
</script>

### 创建 Video.js 封装的自定义组件 为了在 Vue 3 中创建一个基于 Video.js 的自定义组件,可以遵循以下方法。这不仅能够简化视频播放器的集成过程,还能提高代码的重用性和维护性。 #### 安装依赖项 首先,在项目中安装 `video.js` 和其对应的 CSS 文件: ```bash npm install video.js --save ``` 接着引入必要的样式文件至项目的入口文件或全局样式表中: ```css @import '~video.js/dist/video-js.min.css'; ``` #### 编写 VideoPlayer 组件 接下来编写名为 `VideoPlayer.vue` 的单文件组件封装 Video.js 功能。此组件接受一些属性用于配置播放器行为,并处理生命周期事件以初始化和销毁 Video.js 实例。 ```vue <template> <div> <!-- 使用动态绑定 ID 属性 --> <video ref="myVideoPlayer" class="video-js vjs-default-skin" controls preload="auto"> <source :src="props.src" type="video/mp4"/> </video> </div> </template> <script setup> import { onMounted, onBeforeUnmount } from 'vue' import videojs from 'video.js' const props = defineProps({ src: String, }) let player = null; onMounted(() => { // 初始化 Video.js 播放器实例 player = videojs(this.$refs.myVideoPlayer); }); onBeforeUnmount(() => { if (player) { // 清理 Video.js 资源 player.dispose(); } }); </script> ``` 上述代码展示了如何利用 Vue 3 的组合 API 来构建响应式的 Video.js 播放器[^1]。注意这里使用了 `ref` 关键字获取 DOM 元素引用以便传递给 Video.js 进行操作;同时通过监听组件挂载 (`onMounted`) 及卸载前(`onBeforeUnmount`) 阶段完成播放器实例化与释放工作[^2]。 此外,确保每个 `<video>` 标签拥有独一无二的 `id` 值非常重要,这是因为 Video.js 内部会依据这些标识符来进行资源管理和状态跟踪[^3]。 最后,当需要调整默认设置或是添加更多交互逻辑时,则可以通过修改选项参数或者继承原有控件来自由定制所需的功能特性[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值