使用canvas+帧同步实现多张图片生成视频,并附加切换效果

该文章已生成可运行项目,

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
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值