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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值