对进度条进行标记
<template>
<div class="video-progress-tool">
<h1>视频进度条工具</h1>
<el-button type="success" @click="selectVideo">选择视频文件</el-button>
<input ref="videoInput" type="file" @change="handleFileChange" accept="video/*" style="display: none;" />
<div v-if="videoSrc" class="video-container">
<video ref="videoPlayer" :src="videoSrc" controls @timeupdate="checkMarkers" @ended="videoEnded"
style="width: 1000px; height: 500px;"></video>
<div class="progress-container">
<div class="progress-bar"></div>
<div v-for="(marker, index) in markers" :key="index" class="marker"
:style="{ left: marker.position + '%' }" @click="openEditMarkerDialog(marker)">
<div class="marker-content"></div>
</div>
</div>
<el-button type="success" @click="openAddMarkerDialog">添加标记</el-button>
<el-button type="danger" @click="clearMarkers" style="margin-left: 10px;">清除所有标记</el-button>
</div>
<!-- 显示标记内容区域 -->
<div class="marker-display" v-if="currentMarkerContent">
<div class="marker-display-header">
<span>标记内容</span>
<el-icon style="font-size: 20px">
<Close @click="closeMarkerDisplay" class="close-btn" />
</el-icon>
</div>
<div class="marker-display-time">时间: {{ currentMarkerContent.time }}</div>
<div class="marker-display-content">内容: {{ currentMarkerContent.content }}</div>
</div>
<!-- 添加/修改标记弹窗 -->
<el-dialog v-model="dialogFormVisible" :title="dialogTitle" width="500" @close="cancel">
<el-form :model="form">
<el-form-item label="标记内容" label-width='100px'>
<el-input v-model="form.content" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="saveMarker">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const videoPlayer = ref(null);
const videoSrc = ref(null);
const markers = ref([]);
const currentMarkerContent = ref(null);
const videoInput = ref(null);
const currentFileName = ref(''); // 用来存储文件名
const dialogFormVisible = ref(false); // 控制弹窗显示与否
const dialogTitle = ref('添加标记'); // 弹窗标题
const form = ref({
content: '', // 标记内容
});
const selectVideo = () => {
videoInput.value.click();
};
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
const url = URL.createObjectURL(file);
videoSrc.value = url;
currentFileName.value = file.name; // 获取文件名并存储
const savedMarkers = localStorage.getItem(currentFileName.value);
markers.value = savedMarkers ? JSON.parse(savedMarkers) : [];
}
};
const openAddMarkerDialog = () => {
const video = videoPlayer.value;
video.pause(); // 暂停视频播放
dialogTitle.value = '添加标记'; // 设置弹窗标题
form.value.content = ''; // 清空输入框
dialogFormVisible.value = true;
};
const openEditMarkerDialog = (marker) => {
const video = videoPlayer.value;
video.pause(); // 暂停视频播放
dialogTitle.value = '修改标记'; // 设置弹窗标题为“修改标记”
form.value.content = marker.content; // 填充表单内容为点击的标记内容
dialogFormVisible.value = true; // 打开对话框
// 保存当前标记的时间和索引,以便修改时更新它
form.value.originalTime = marker.time;
form.value.markerIndex = markers.value.findIndex(m => m.time === marker.time);
};
const saveMarker = () => {
const video = videoPlayer.value;
const currentTime = video.currentTime;
const position = (currentTime / video.duration) * 100;
let newMarker;
if (dialogTitle.value === '添加标记') {
// 添加新标记时使用当前的时间和位置
newMarker = {
position: position,
time: formatTime(currentTime),
content: form.value.content,
};
markers.value.push(newMarker); // 将新标记加入标记列表
} else if (dialogTitle.value === '修改标记') {
// 修改标记时,保留原来的位置和时间,只更新内容
const markerIndex = form.value.markerIndex;
if (markerIndex !== undefined && markerIndex !== -1) {
newMarker = {
position: markers.value[markerIndex].position, // 保留原标记的 `position`
time: markers.value[markerIndex].time, // 保留原标记的 `time`
content: form.value.content, // 使用修改后的内容
};
markers.value[markerIndex] = newMarker; // 更新标记
}
}
saveMarkers(); // 更新 localStorage 中的标记
dialogFormVisible.value = false; // 关闭弹窗
currentMarkerContent.value = null;//关闭标记显示
video.play(); // 继续播放视频
};
const saveMarkers = () => {
if (videoSrc.value && currentFileName.value) {
localStorage.setItem(currentFileName.value, JSON.stringify(markers.value)); // 更新 localStorage
}
};
const cancel = () => {
dialogFormVisible.value = false;
const video = videoPlayer.value;
video.play();
}
const formatTime = (time) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
const checkMarkers = () => {
const currentTime = videoPlayer.value.currentTime;
const tolerance = 0.3; // 容差值为 0.1 秒
markers.value.forEach((marker) => {
const markerTime = parseTime(marker.time);
if (Math.abs(currentTime - markerTime) <= tolerance) {
currentMarkerContent.value = { time: marker.time, content: marker.content };
}
});
};
const parseTime = (timeStr) => {
const [minutes, seconds] = timeStr.split(':').map(Number);
return minutes * 60 + seconds;
};
const videoEnded = () => {
currentMarkerContent.value = null; // 视频播放结束时隐藏显示框
};
const clearMarkers = () => {
if (currentFileName.value) {
localStorage.removeItem(currentFileName.value);
markers.value = []; // 清空当前标记数据
currentMarkerContent.value = null;
ElMessage({
message: '所有标记已清除',
type: 'success',
});
}
};
const closeMarkerDisplay = () => {
currentMarkerContent.value = null;
};
onMounted(() => {
if (currentFileName.value) {
const savedMarkers = localStorage.getItem(currentFileName.value);
if (savedMarkers) {
markers.value = JSON.parse(savedMarkers);
}
}
});
</script>
<style lang="less" scoped>
.video-progress-tool {
text-align: center;
margin: 20px;
}
.video-container {
position: relative;
width: 1000px;
margin: 20px auto;
}
video {
width: 500px;
height: 300px;
margin: 0 auto;
}
.progress-container {
position: relative;
width: 970px;
height: 10px;
background: rgba(0, 0, 0, 0);
margin: 0 auto;
cursor: pointer;
top: -12px;
}
.progress-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.marker {
position: absolute;
top: -10px;
width: 10px;
height: 30px;
background: red;
transform: translateX(-50%);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
}
.marker-content {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100px;
}
.marker-display {
width: 500px;
margin: 20px auto;
padding: 10px;
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
text-align: center;
font-size: 14px;
color: #333;
position: relative;
}
.marker-display-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.close-btn {
font-size: 20px;
cursor: pointer;
color: #000;
}
</style>