Vue3 视频打点业务解决方案详解

Vue3 视频打点业务解决方案详解

一、核心业务场景
  1. 教育视频关键帧标记
  2. 用户UGC内容精彩片段标注
  3. 视频审核问题点位记录
  4. 影视制作关键帧注释
二、技术方案架构
核心依赖:
- @vueuse/core(推荐)
- video.js(可选)
- 原生HTML5 Video
三、基础实现方案
<template>
  <div class="video-container">
    <video ref="videoRef" controls class="video-player">
      <source src="/demo.mp4" type="video/mp4">
    </video>
    
    <div class="controls">
      <button @click="addMarker">添加标记</button>
      <button @click="saveMarkers">保存标记</button>
    </div>

    <div class="marker-list">
      <div v-for="(marker, index) in markers" :key="index" 
           class="marker-item" @click="jumpTo(marker.time)">
        {{ formatTime(marker.time) }} - {{ marker.comment }}
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue'
import { useMediaControls } from '@vueuse/core'

// 视频元素引用
const videoRef = ref(null)
const markers = reactive([])

// 使用VueUse的媒体控制
const { currentTime, duration } = useMediaControls(videoRef)

// 添加标记点
const addMarker = () => {
  if (!videoRef.value) return
  
  const marker = {
    time: currentTime.value,
    comment: prompt('请输入标记备注') || '未命名标记'
  }
  
  markers.push(marker)
  markers.sort((a, b) => a.time - b.time)
}

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

// 时间格式化
const formatTime = (seconds) => {
  const date = new Date(0)
  date.setSeconds(seconds)
  return date.toISOString().substr(11, 8)
}

// 保存标记点
const saveMarkers = () => {
  localStorage.setItem('video_markers', JSON.stringify(markers))
}

// 初始化加载
onMounted(() => {
  const saved = localStorage.getItem('video_markers')
  if (saved) markers.push(...JSON.parse(saved))
})

// 清理事件监听
onUnmounted(() => {
  if (videoRef.value) {
    videoRef.value.pause()
    videoRef.value.removeAttribute('src')
    videoRef.value.load()
  }
})
</script>

<style scoped>
.video-container {
  max-width: 800px;
  margin: 0 auto;
}

.video-player {
  width: 100%;
  height: 450px;
}

.controls {
  margin: 15px 0;
  display: flex;
  gap: 10px;
}

.marker-list {
  border-top: 1px solid #ddd;
  padding-top: 15px;
}

.marker-item {
  padding: 8px;
  margin: 5px 0;
  cursor: pointer;
  background: #f5f5f5;
  transition: background 0.3s;
}

.marker-item:hover {
  background: #e0e0e0;
}
</style>
四、高级功能实现
  1. 可视化进度条标记
<template>
  <div class="progress-bar" @click="seek">
    <div class="progress" :style="{ width: progressPercent }">
      <div v-for="marker in markers" 
           :key="marker.time"
           class="marker" 
           :style="{ left: getMarkerPosition(marker.time) }"
           @click.stop="jumpTo(marker.time)">
      </div>
    </div>
  </div>
</template>

<script setup>
// 进度计算
const progressPercent = computed(() => 
  `${(currentTime.value / duration.value * 100) || 0}%`
)

const getMarkerPosition = (time) => 
  `${(time / duration.value * 100)}%`

const seek = (e) => {
  const rect = e.target.getBoundingClientRect()
  const percent = (e.clientX - rect.left) / rect.width
  videoRef.value.currentTime = percent * duration.value
}
</script>

<style>
.progress-bar {
  height: 5px;
  background: #ddd;
  cursor: pointer;
 position: relative;
}

.progress {
  height: 100%;
  background: #42b983;
  position: relative;
}

.marker {
  position: absolute;
  width: 8px;
  height: 8px;
  background: #ff4757;
  border-radius: 50%;
  top: -2px;
  transform: translateX(-50%);
}
</style>
  1. 标记点协同编辑(WebSocket集成)
// 添加WebSocket支持
import { useWebSocket } from '@vueuse/core'

const { data, send } = useWebSocket('ws://your-websocket-url')

watch(data, (newData) => {
  try {
    const msg = JSON.parse(newData)
    if (msg.type === 'MARKER_UPDATE') {
      markers.splice(0, markers.length, ...msg.data)
    }
  } catch(e) {
    console.error('WS数据解析失败', e)
  }
})

const broadcastMarkers = () => {
  send(JSON.stringify({
    type: 'MARKER_UPDATE',
    data: markers
  }))
}
五、常见问题解决方案
  1. 时间精度问题
// 使用高精度时间处理
const getCurrentTime = () => {
  return videoRef.value ? Math.floor(videoRef.value.currentTime * 100) / 100 : 0
}
  1. 大数量标记性能优化
// 虚拟滚动优化
import { useVirtualList } from '@vueuse/core'

const { list, containerProps, wrapperProps } = useVirtualList(
  markers,
  {
    itemHeight: 40,
    overscan: 10
  }
)
  1. 跨浏览器兼容性
// 全屏处理兼容
const requestFullscreen = () => {
  if (videoRef.value.requestFullscreen) {
    videoRef.value.requestFullscreen()
  } else if (videoRef.value.webkitRequestFullscreen) {
    videoRef.value.webkitRequestFullscreen()
  }
}
  1. 视频预加载优化
<video preload="metadata">
  <source src="/video.mp4#t=0.5" type="video/mp4">
</video>
六、最佳实践建议
  1. 数据持久化策略
  • 使用IndexedDB存储大量标记数据
  • 配合LocalStorage实现自动保存
  • 重要数据增加服务端同步
  1. 性能优化方案
// 防抖处理标记更新
const updateMarkers = useDebounceFn((newMarkers) => {
  sendUpdateToServer(newMarkers)
}, 1000)
  1. 安全增强
// XSS防护
const sanitizeComment = (text) => {
  return text.replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
  1. 可访问性增强
<button aria-label="添加视频标记" @click="addMarker">
  <svg><!-- 图标 --></svg>
</button>
七、扩展功能建议
  1. 标记分类系统
const markerTypes = {
  IMPORTANT: { color: '#ff4757' },
  COMMENT: { color: '#2ed573' },
  QUESTION: { color: '#ffa502' }
}
  1. AI自动标记
const detectScenes = async () => {
  const response = await fetch('/api/detect-scenes', {
    method: 'POST',
    body: videoFile
  })
  const scenes = await response.json()
  markers.push(...scenes)
}
  1. 视频帧精确预览
const generateThumbnail = async (time) => {
  const canvas = document.createElement('canvas')
  canvas.width = 160
  canvas.height = 90
  videoRef.value.currentTime = time
  await new Promise(r => videoRef.value.onseeked = r)
  canvas.getContext('2d').drawImage(videoRef.value, 0, 0)
  return canvas.toDataURL()
}

该方案完整实现了视频打点的核心功能,并提供了扩展性良好的架构设计,可根据具体业务需求进行模块化扩展。建议在实际项目中根据具体需求选择合适的第三方库和优化策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

繁若华尘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值