1、生成普通视频
const imgListtoVideoFn = async (list,callback) => {
// list 图片数组
if(!list||!list.length)return
const frames = [];
for (let i = 0; i < list.length; i++) {
const imgUrl = list[i];
// 创建新的图片元素
const img = new Image();
// 解决跨域问题(如果图片来自不同域名)
img.crossOrigin = 'anonymous';
// 等待图片加载完成
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = imgUrl;
});
frames.push(img);
}
// 创建隐藏的canvas元素
// 这个canvas将用于绘制每一帧的图像并录制视频
const canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 800;
const ctx = canvas.getContext("2d");
// 设置MediaRecorder来捕获canvas流
// captureStream()方法将canvas的绘制内容捕获为媒体流
const stream = canvas.captureStream();
// 创建MediaRecorder实例,用于录制视频
// 设置视频的MIME类型为video/webm
const recorder = new MediaRecorder(stream, {
mimeType: "video/mp4"
});
const chunks = [];
// 当有数据可用时,将数据块添加到chunks数组中
recorder.ondataavailable = (event) => {
if (event.data.size > 0) {
chunks.push(event.data);
}
};
// 当录制停止时,将所有数据块合并为一个Blob对象,并保存为视频文件
recorder.onstop = () => {
const blob = new Blob(chunks, {
type: "video/mp4"
});
// 创建视频的URL
const url = URL.createObjectURL(blob);
callback(blob,url)
};
// 帧同步核心逻辑
recorder.start();
const frameDuration = 2000; // 每帧显示2000ms
let currentFrameIndex = 0; // 当前帧索引
const startTime = performance.now(); // 记录开始时间(高精度计时)
let lastFrameTime = startTime; // 上一帧开始时间
// 定义帧更新函数
const updateFrame = (timestamp) => {
// 计算从开始到现在的总耗时
const elapsed = timestamp - startTime;
// 计算当前帧已显示的时间
const currentFrameElapsed = timestamp - lastFrameTime;
// 如果当前帧显示时间达到设定时长,切换到下一帧
if (currentFrameElapsed >= frameDuration) {
currentFrameIndex++;
lastFrameTime = timestamp; // 更新上一帧时间为当前时间
}
// 绘制当前帧(如果还有帧未显示)
if (currentFrameIndex < frames.length) {
const frame = frames[currentFrameIndex];
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 居中缩放绘制
const scale = Math.min(canvas.width / frame.width, canvas.height / frame.height);
const x = (canvas.width - frame.width * scale) / 2;
const y = (canvas.height - frame.height * scale) / 2;
ctx.drawImage(frame, x, y, frame.width * scale, frame.height * scale);
// 继续请求下一帧
requestAnimationFrame(updateFrame);
} else {
// 所有帧绘制完成,停止录制
recorder.stop();
}
};
// 启动帧同步
requestAnimationFrame(updateFrame);
};
2、切换效果-淡入淡出
// 生成的轮播图-淡入淡出效果
const imgListtoVideoFnFade = async (list, callback) => {
if (!list || !list.length) return;
const frames = [];
for (let i = 0; i < list.length; i++) {
const imgUrl = list[i];
const img = new Image();
img.crossOrigin = 'anonymous';
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = imgUrl;
});
frames.push(img);
}
const canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 800;
const ctx = canvas.getContext("2d");
const stream = canvas.captureStream(30);
const recorder = new MediaRecorder(stream, {
mimeType: "video/mp4",
width: canvas.width,
height: canvas.height
});
const chunks = [];
recorder.ondataavailable = (event) => {
if (event.data.size > 0) {
chunks.push(event.data);
}
};
recorder.onstop = () => {
const blob = new Blob(chunks, { type: "video/mp4" });
const url = URL.createObjectURL(blob);
ca