javascript
复制代码
<template>
<div>
<div>
<video ref="videoRef" :src="theUrl" controls autoplay muted crossOrigin="anonymous"></video>
<!-- <div class="controls">
<button :disabled="isRecording" @click="startCapture">
开始录制
</button>
<button :disabled="!isRecording" @click="stopCapture">
停止录制
</button>
</div> -->
<!-- <div v-if="gifUrl" class="output">
<h3>录制的GIF:</h3>
<img :src="gifUrl" alt="GIF 预览" />
</div> -->
</div>
</div>
</template>
<script setup>
import request from '/@/utils/request';
import GIF from 'gif.js'
const emit = defineEmits(['sendGif']);
const props = defineProps({
fileUrl: {
type: String,
default: ''
}
})
let theUrl = ref('')
let timerId = ref(null) // 新增定时器ID
// 监听视频URL变化
watch(() => props.fileUrl, (newVal) => {
theUrl.value = newVal
nextTick(() => {
if (videoRef.value) {
// 清除之前的监听
videoRef.value.removeEventListener('loadeddata', handleVideoLoaded)
// 添加新的监听
videoRef.value.addEventListener('loadeddata', handleVideoLoaded)
}
})
})
// 视频加载完成后自动开始录制
const handleVideoLoaded = () => {
console.log('视频加载完成,开始录制')
// 视频从 20秒 开始播放,即:从20秒开始截取
videoRef.value.currentTime = 20;
startCapture()
// 移除监听,避免重复调用
videoRef.value.removeEventListener('loadeddata', handleVideoLoaded)
}
const videoRef = ref(null)
const isRecording = ref(false)
const progress = ref(0)
const gifUrl = ref(null)
let gifRecorder = null
let captureInterval = null
let gifBlob = ref(null);
// 初始化GIF录制器
const initGifRecorder = () => {
if (gifRecorder) {
gifRecorder.abort()
}
gifRecorder = new GIF({
workers: 2,
quality: 10,
width: videoRef.value?.videoWidth || 320,
height: videoRef.value?.videoHeight || 240,
workerScript: '/gif.worker.js'
})
gifRecorder.on('progress', p => {
progress.value = Math.round(p * 100)
})
gifRecorder.on('finished', blob => {
gifUrl.value = URL.createObjectURL(blob)
isRecording.value = false
progress.value = 0
console.log('gifUrl=', gifUrl.value);
// emit('sendGif', gifUrl.value)
//#region Blob转Base64
// const reader = new FileReader();
// reader.onload = () => {
// // const base64Data = reader.result.replace(/^data:image\/\w+;base64,/, '');
// // emit('sendGif', base64Data);
// const base64Data = reader.result;
// // console.log('base64Data=', base64Data);
// emit('sendGif', base64Data);
// };
// reader.onerror = (e) => {
// console.error('Base64转换失败:', e);
// emit('sendGif', null); // 发送失败信号
// };
// reader.readAsDataURL(blob);
//#endregion
//#region Blob转换为file文件
gifBlob.value = blob;
let file = new File(
[gifBlob.value],
`recording-${Date.now()}.gif`,
{ type: 'image/gif' }
);
console.log('生成的file=', file);
uploadGif(file)
})
}
// 捕获帧
const captureFrame = () => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// 跨域安全验证
if (!videoRef.value || !videoRef.value.crossOrigin) {
console.error('视频元素未配置跨域访问!')
return
}
canvas.width = videoRef.value.videoWidth
canvas.height = videoRef.value.videoHeight
ctx.drawImage(videoRef.value, 0, 0)
gifRecorder.addFrame(canvas, {
delay: 100,
copy: true
})
}
// 开始录制
const startCapture = () => {
if (!videoRef.value || isRecording.value) return
initGifRecorder()
isRecording.value = true
// 每100ms捕获一帧
captureInterval = setInterval(captureFrame, 100)
// 设置5秒后自动停止录制
if (timerId.value) {
clearTimeout(timerId.value)
}
timerId.value = setTimeout(() => {
stopCapture()
timerId.value = null
}, 5000)
}
// 停止录制
const stopCapture = () => {
if (!isRecording.value) return
clearInterval(captureInterval)
gifRecorder.render()
console.log('录制结束,生成GIF')
// 清除定时器
if (timerId.value) {
clearTimeout(timerId.value)
timerId.value = null
}
}
// 清理资源
onUnmounted(() => {
if (gifRecorder) {
gifRecorder.abort()
}
clearInterval(captureInterval)
if (timerId.value) {
clearTimeout(timerId.value)
}
})
// 上传接口
const uploadGif = async (file) => {
const { data } = await request({
url: '/admin/sys-file/upload',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
'Enc-Flag': 'false',
},
data: { file },
});
console.log('data=', data);
if (data) {
emit('sendGif', data.url);
}
}
</script>
<style scoped>
.controls {
margin: 1rem 0;
display: flex;
gap: 1rem;
}
.progress {
margin-top: 1rem;
}
.output {
margin-top: 1rem;
}
.frame img {
width: 100px;
height: auto;
}
</style>