用户通过上传合适尺寸的图片,选着渲染动画的效果和音乐,可以预览类似幻灯片的效果,最后点击确认生成视频,可以放到头条或者抖音播放。

生成视频可能的方案

纯前端的视频编码转换(例如WebM Encoder Whammy)

图片地址只能是相对地址 音乐不能收录 生成的视频需要下载再上传

将每帧图片传给后端实现,由后端调用FFmpeg进行视频转码

截图多的时候,base64字符串形式的图片太大,在前端不好传给后端 在前端截图还依赖用户电脑性能;

最后定的方案流程

canvas动画和截图在服务器端运行,后端根据标识获取截图 利用FFmpeg将图片合并成视频,并将视频存储在server端,并返回相应下载url 前端通过请求得到视频文件

前端canvas如何截图

每帧图片生成

图片生成可以通过canvas原生接口toDataURL实现,最终返回base64形式的图像数据

function generatePng() {    var canvas = document.createElement(canvas);    let icavas = #canvas //渲染动画的canvas id    if (wrapWidth == 2) {        icavas = #verticalCanvas    }    var canvasNode = document.querySelector(icavas)    canvas.width = canvasNode.width;    canvas.height = canvasNode.height;    var ctx = canvas.getContext(2d);    ctx.drawImage(canvasNode, 0, 0);    var imgData = canvas.toDataURL("image/png");    return imgData;}

canvas动画截图的方法

用setInterval定时执行图片生成的方法,当然也可以用requestAnimationFrame

setInterval(function() {    imgsTemp.push(generatePng())}, 1000/60)

后端如何获取每帧图片

方案一:无头浏览器运行前端canvas动画js,然后js截图

最初设想:

截图用console.log打印出来,canvas截图是base64格式的,一个15秒的动画,截图有100多张,直接导致服务器运行崩溃(被否了);

试运行方案:

截图存储在js变量中,动画播放完成,在页面中加一个标识,然后后端去取这个变量,代码如下:

const pages = {    imageZoomOut: import (./image_zoom_inout.js), //缩放    imageArt: import (./image_art.js), //擦除    imageGrid: import (./image_grid.js), //网格    imageRotate: import (./image_rotate.js), //开合    imageFlash: import (./image_flash.js), //图文快闪    imageVerticalArt: import (./image_vertical_art.js), //竖版擦除    imageVerticalGrid: import (./image_vertical_grid.js), //竖版网格    imageVerticalRotate: import (./image_vertical_rotate.js), //竖版开合    imageVerticalFlash: import (./image_vertical_flash.js), //竖版图文快闪    imageVerticalZoomOut: import (./image_vertical_zoom_inout.js), //竖版缩放    imageVertical: import (./image_vertical.js), //竖版通用};var isShow = falsevar imgsBase64 = []var imgsTemp = []var cutInter = nullvar imgsTimeLong = 0function getQuerys(tag) {    let queryStr = window.location.search.slice(1);    let queryArr = queryStr.split(&);    let query = [];    let spec = {}    for (let i = 0, len = queryArr.length; i < len; i++) {        let queryItem = queryArr[i].split(=);        let qitem = decodeURIComponent(queryItem[1])        if (queryItem[0] == tag) {            query.push(qitem);        } else {            spec[queryItem[0]] = qitem        }    }    return { list: query, spec: spec };}var getQuery = getQuerys(images)var effectTag = getQuery.spec.tidvar wrapWidth = getQuery.spec.templateTypelet num = 0let imgArr = []function creatImg() {    var images = getQuery.list    let newImg = []    let vh = wrapWidth == 1 ? 360 : 640    let vw = wrapWidth == 1 ? 640 : 360    if (effectTag.indexOf(Flash) > -1) {        images.map(function(item, index) {            if (11 === index || 13 === index || 16 === index) {                var temp = new Image(vw, vh)                temp.setAttribute(crossOrigin, anonymous);                temp.src = item;                newImg.push(temp)            } else {                newImg.push(item)            }        })        imgArr = newImg        renderAnimate(effectTag)    } else {        images.map(function(item) {            var temp = new Image(vw, vh)            temp.setAttribute(crossOrigin, anonymous);            temp.src = item;            temp.onload = function() {                num++                if (num == images.length) {                    renderAnimate(effectTag)                }            }            newImg.push(temp)        })        imgArr = newImg    }}async function renderAnimate(page) {    //await creatImg()    let me = this    const pageA = await pages 
        

前端canvas动画如何转成mp4视频的方法