Canvas动画制作并转为视频合并音乐

24 篇文章 1 订阅
1、canvas动画说明

10种动画每种都可以拆分为前后两部分; 前部分为第一张做动作(缩放、左右上下移动), 后一部分为第二张图片进场,进场方式多种根据特效而定;

各动画的具体实现参考各动画的代码

用到的主要api : 切片 drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

img规定要使用的图像、画布或视频。
sx可选。开始剪切的 x 坐标位置。
sy可选。开始剪切的 y 坐标位置。
swidth可选。被剪切图像的宽度。
sheight可选。被剪切图像的高度。
x在画布上放置图像的 x 坐标位置。
y在画布上放置图像的 y 坐标位置。
width可选。要使用的图像的宽度。(伸展或缩小图像)
height可选。要使用的图像的高度。(伸展或缩小图像)

主要关键点: 动画暂停、图片比例兼容和3:4制作兼容、时长控制

动画暂停只在用户操作页面上需要该功能;
实现思路:首先弄一个变量来判断是暂停还是播放状态,动画实现是通过定时任务一直重复调动画方法,直到达到某条件才结束;
因此实现暂停是在每个动画方法开头进行暂停判断,如果是暂停状态就 将当前动画的一些参数存入全局变量 然后return动画方法; 继续的话同样调动画方法,只不过需要将动画的特有参数从全局变量中取出来,从而实现动画的继续渲染;

图片比例兼容和3:4兼容,图片不能变形,确定好怎么截取图片,
设计要点:动画画布必须被铺满、图片只能等比缩放(不能变形)、如果图片取不完,则取图片中心区域(高度多了就取中间部分,宽度多了同理)

时长控制: 目前所有动画四张图片时长误差在100毫秒之内,即9秒视频的误差在100毫秒之内;
定时任务最终采用的setTimeout;用过requestAnimationFrame测试,达不到定时效果,它跑完之后直接继续跑,如果要改成这个 需要去微调动画 “递归” 退出条件的增长量或减少量;

2、动画转视频 视频转码 合成音频 以及上传

动画转视频

参考api : MediaStream = canvas.captureStream(frameRate);

能将canvas画布上的动画转为视频流,问题是需要动画播放完(即动画多长转多长),考虑到用户不会等或进行其他操作,所以单独在其他项目渲染动画再转为视频;

视频转码

前端转码参考 FFmpeg
在这里插入图片描述

他的api run方法里面能直接使用 ffmpeg 的 转码命令(语法稍微不一样,空格变成逗号)

代码 (template-renderer项目)

//转MP4、合成音频
    async getMp4(blob) {
      let startTime = new Date()
      let file = new window.File(blob, 'webmVideo', {
        type: 'video/webm'
      })


      const { createFFmpeg, fetchFile } = FFmpeg
      const ffmpeg = createFFmpeg({
        log: true,
        // eslint-disable-next-line no-unused-vars
        progress: ({ ratio }) => {
          // console.log(`Complete: ${(ratio * 100.0).toFixed(2)}%`)
        }
      })

      const { name } = file
      await ffmpeg.load()
      ffmpeg.FS('writeFile', name, await fetchFile(file))
      let musicBlob = await this.getMusic()

      //转码  webm => mp4
      await ffmpeg.run('-i', name, 'outputMp4Video.mp4')
      console.log('BBT 转码完成')
      //构建音频
      // let musicBlob = await this.getMusic()
      let fileMusic = new window.File(musicBlob, 'mp3Music', {
        type: 'audio/mpeg'
      })
      const nameMusic = 'mp3Music'
      ffmpeg.FS('writeFile', nameMusic, await fetchFile(fileMusic))
      console.log('BBT 加载音乐完毕')
      //剪切音乐
      let musicTime = Math.ceil(this.images.length * 2.25)
      musicTime = musicTime < 10 ? `0${musicTime}` : musicTime
      // # ffmpeg -i aa.mp3 -ss 00:01:12 -t 00:01:42 -acodec copy bb.mp3
      await ffmpeg.run(
        '-i',
        nameMusic,
        '-ss',
        '00:00:00',
        '-t',
        `00:00:${musicTime}`,
        '-acodec',
        'copy',
        'mp3MusicNew.mp3'
      )
      //加背景音乐
      // ffmpeg -i 1.mp3 -i 1.mp4 outputname.mp4
      await ffmpeg.run(
        '-i',
        'mp3MusicNew.mp3',
        '-i',
        'outputMp4Video.mp4',
        'endMusicMp4Video.mp4'
      )
      console.log('BBT 剪切背景音乐 合成音乐 结束')

      //最终数据
      const data = ffmpeg.FS('readFile', 'endMusicMp4Video.mp4')
      console.log('BBT 加载最终视频文件')
      // let byte = new Blob([data.buffer], {
      //   type: 'video/mp4'
      // })
      let aTemp = document.createElement('a')
      aTemp.href = URL.createObjectURL(
        new Blob([data.buffer], {
          type: 'video/mp4'
        })
      )
      aTemp.download = '40Test.mp4'
      aTemp.click()
      const video = document.getElementById('video-mp4')
      video.src = URL.createObjectURL(
        new Blob([data.buffer], {
          type: 'video/mp4'
        })
      )
      let endTime = new Date()
      let mm = endTime.getTime() - startTime.getTime()
      console.log('视频时长:', this.videoDuration, ';转换时间:', mm / 1000)
    },
    //加载音乐资源
    getMusic() {
      let that = this
      return new Promise(function(res, rej) {
        let musicBlob = []
        let xhr = new XMLHttpRequest()
        xhr.open('get', that.musicUrl, true)
        xhr.responseType = 'blob'
        xhr.onload = function() {
          if (xhr.status == 200) {
            musicBlob.push(xhr.response)
            console.log(musicBlob)
            res(musicBlob)
          } else {
            rej()
          }
        }
        xhr.send()
      })
    },

后端转码 使用的 javacv

参考博客 转码 合并音频 其他 JAVE2

上传

后台将最终视频文件上传到oss

3、项目功能简述

涉及项目:用户操作页面、后台、node服务(模拟浏览器项目)、renderer页面(渲染项目)

用户操作页面:让用户直观看到“视频”效果、实现暂停继续播放、音频同步播放暂停、视频制作请求后台

后台:接收请求、存/更新记录到数据库、转调服务(调node服务)、视频处理(视频转码音频合并)、上传视频到oss

node服务:模拟浏览器行为,通过url打开页面并调用其方法(打开renderer渲染页面,让其开始渲染动画)

renderer页面:渲染动画、将动画转为视频流然后发送给后台处理

动画代码示例

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#老程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值