视频剪辑工具-进度条管理

对进度条进行标记

<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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值