Files
Format-Converter/src/thumbnail-helper.js
2026-02-06 12:39:11 +06:00

124 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 缩略图生成辅助函数
* 当后端 FFmpeg 生成失败时,使用浏览器原生能力作为兜底
*/
/**
* 使用浏览器原生 API 生成视频缩略图
* @param {File} file - 视频文件对象
* @param {number} seekTime - 截取时间点(秒)
* @returns {Promise<string>} base64 图片数据
*/
export async function generateVideoThumbnailNative(file, seekTime = 0.1) {
return new Promise((resolve, reject) => {
const video = document.createElement('video');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置视频源
const url = URL.createObjectURL(file);
video.src = url;
video.muted = true;
video.playsInline = true;
video.addEventListener('loadeddata', () => {
// seek 到指定时间
video.currentTime = Math.min(seekTime, video.duration * 0.1 || 0.1);
});
video.addEventListener('seeked', () => {
try {
// 计算缩放后的尺寸(最大宽度 320
const maxWidth = 320;
const scale = Math.min(1, maxWidth / video.videoWidth);
canvas.width = video.videoWidth * scale;
canvas.height = video.videoHeight * scale;
// 绘制帧
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// 转换为 base64
const dataUrl = canvas.toDataURL('image/jpeg', 0.85);
URL.revokeObjectURL(url);
resolve(dataUrl);
} catch (e) {
URL.revokeObjectURL(url);
reject(e);
}
});
video.addEventListener('error', (e) => {
URL.revokeObjectURL(url);
reject(new Error('视频加载失败'));
});
// 开始加载
video.load();
});
}
/**
* 使用浏览器原生 API 生成图片缩略图
* @param {File} file - 图片文件对象
* @returns {Promise<string>} base64 图片数据
*/
export async function generateImageThumbnailNative(file) {
return new Promise((resolve, reject) => {
const img = new Image();
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const url = URL.createObjectURL(file);
img.onload = () => {
try {
// 计算缩放后的尺寸(最大宽度 320
const maxWidth = 320;
const scale = Math.min(1, maxWidth / img.naturalWidth);
canvas.width = img.naturalWidth * scale;
canvas.height = img.naturalHeight * scale;
// 绘制图片
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// 转换为 base64
const dataUrl = canvas.toDataURL('image/jpeg', 0.85);
URL.revokeObjectURL(url);
resolve(dataUrl);
} catch (e) {
URL.revokeObjectURL(url);
reject(e);
}
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('图片加载失败'));
};
img.src = url;
});
}
/**
* 智能缩略图生成
* 优先使用后端 FFmpeg失败时尝试浏览器原生方案
*/
export async function generateThumbnailSmart(filePath, fileType, fileObj = null) {
// 首先尝试使用后端 FFmpeg如果有 file 对象的话)
if (fileObj && fileObj instanceof File) {
try {
if (fileType === 'video') {
return await generateVideoThumbnailNative(fileObj);
} else if (fileType === 'image') {
return await generateImageThumbnailNative(fileObj);
}
} catch (e) {
console.log('浏览器原生缩略图生成失败,使用后端:', e);
}
}
// 返回 null让后端处理
return null;
}