当前位置: 首页 > news >正文

如何利用纯前端技术,实现一个网页版视频编辑器?

纯网页版视频编辑器

  • 一、前言
  • 二、功能实现
  • 三、所需技术
  • 四、部分功能实现
    • 4.1 素材预设
    • 4.2 多轨道剪辑

一、前言

介绍:本篇文章打算利用纯前端的技术,来实现一个网页版的视频编辑器。为什么突然想做一个这么项目来呢,主要是最近一直在利用手机剪映来剪辑一些照片或者视频之类的,在剪辑的过程中,突然想到,有没有一种纯网页版的视频剪辑网站呢?于是搜了下,大多为 sass 成熟版(需要花钱的那种),然后再加上最近一直在看前端技术,于是就打算利用现学的前端技术,来实现一个纯前端的纯网页版的视频编辑器demo。

先给大家看下整体效果图:

在这里插入图片描述
tips:整体看上去像模像样的。

二、功能实现

这里就先简单列下具体的功能包括哪些:

  • 支持深色模式(白天/黑夜)

  • 支持云素材(暂为mock模拟)以及本地上传素材

  • 支持拖拽添加资源

  • 支持多轨道

  • 支持表单调整资源位置、属性

  • 支持音视频裁剪

  • 支持手动添加贴图、文字

  • 支持时间轴缩放(ctrl+滚轮),最多显示30帧

  • 支持播放预览

  • 支持导出

  • 支持操作撤销、重做功能

  • 支持持久化存储功能

三、所需技术

这里也先简单列下项目中具体用到的技术包括哪些:

  • axios(^1.4.0)
  • element-plus(^2.3.4)
  • mockjs (^1.1.0)
  • pinia (^2.1.3)
  • vue(^3.2.47)
  • typescript(^5.0.2)
  • vite(^4.3.2)

插件包括:

  • commitlint(^17.6.3)
  • ffmpeg(^0.11.6)核心插件
  • cross-env(^7.0.3)
  • eslint(^8.40.0)
  • husky(^8.0.3)
  • postcss(^8.4.23)
  • prettier(^2.8.8)
  • stylelint(^15.6.1)
  • types/node(^20.1.4)
  • element-plus(^2.1.0)

四、部分功能实现

4.1 素材预设

素材预设功能,我们这里是利用了 mock 技术,来代替后端传输的数据。

先利用mock 来模拟一些素材或者进行预设,比如:

const mockMethods: MockMethod[] = [{url: '/api/getResources',method: 'get',response: ({ query }) => {const type = query.typelet data: ResourcesList = []if (type === 'video') {data = [{title: '转场',type: 'video',items: [{name: '故障雪花屏.mp4',format: 'mp4',cover: '/image/video/故障雪花屏.jpg',source: '/video/故障雪花屏.mp4',width: 1920,height: 1080,fps: 30,frameCount: 30,time: 1000}]}]} else if (type === 'audio') {data = [{title: '旋律',type: 'audio',items: [{cover: '/image/audio/Charms.jpg',time: 244000,format: 'mp3',name: 'Charms.mp3',singer: 'Abel Korzeniowski',source: '/audio/Abel Korzeniowski - Charms.mp3'}]}]} else if (type === 'text') {data = [{title: '热门',type: 'text',items: [{name: 'CherryBombOne.ttf',templateId: 0,source: '/text/CherryBombOne-Regular.ttf',format: 'truetype'}}]} else if (type === 'image') {data = [{title: '热门',type: 'image',items: [{name: '666.gif',cover: '/image/image/666.gif',source: '/image/image/666.gif',format: 'gif',width: 199,height: 200,sourceFrame: 8}},{title: '经典',type: 'image',items: [{name: '喇叭.gif',cover: '/image/image/喇叭.gif',source: '/image/image/喇叭.gif',format: 'gif',width: 199,height: 200,sourceFrame: 6},{name: '马赛克.gif',cover: '/image/image/马赛克.gif',source: '/image/image/马赛克.gif',format: 'gif',width: 199,height: 200,sourceFrame: 6},{name: '马赛克小人.gif',cover: '/image/image/马赛克小人.gif',source: '/image/image/马赛克小人.gif',format: 'gif',width: 199,height: 200,sourceFrame: 6},{name: '闪光.gif',cover: '/image/image/闪光.gif',source: '/image/image/闪光.gif',format: 'gif',width: 199,height: 200,sourceFrame: 6}]}return {code: 200,data}}}
]export default mockMethods

代码写完后,不要忘记把素材也要放到项目文件夹里

在这里插入图片描述

4.2 多轨道剪辑

什么是多轨道剪辑?

多轨道编辑即是将不同的素材放置在不同的轨道上,通过调整它们在时间线上的位置和长度,达到叠加、剪辑和混合的效果。 我们可以通过拖拽素材到时间线上的不同轨道来进行多轨道编辑。

通常情况下,视频素材放置在视频轨道上,音频素材放置在音频轨道上。这样,我们可以通过调整素材在时间线上的位置和长度来控制视频和音频的播放顺序、时长和重叠关系

从技术角度来实现的话,这里就通过用 ffmpeg 技术,来实现 多轨道剪辑功能。

  1. 首先创建一个任务队列对象,来存储多轨道的数据,比如视频、音乐、文本等等素材轨道。
  private ffmpeg: FFmpegprivate taskQueue = reactive<Task[]>([]) // 任务队列private running = ref(false) // 运行状态public showLog = truepublic playTimeCache = new Map()public audioCache: string[] = []public baseCommand = new Command()
  1. 然后我们可以对其创建任务,并判断任务队列中是否有执行任务的命令,如果有则返回任务存在,如果没有则返回 undefined。
 createTask(commands: string[]) {const task = this.existTask(commands)if (task) {return task.instance} else {const callbacks = {}const instance = new Promise((resolve, reject) => {Object.assign(callbacks, {resolve,reject})})this.taskQueue.push({instance,commands,...callbacks} as Task)return instance}}
  1. 用户把素材资源从本地拖拽到页面内,需要获取到文件内容
 // 获取文件BlobgetFileBlob(filePath: string, fileName: string, format: string) {const fileBuffer = this.getFileBuffer(filePath, fileName, format)return new Blob([fileBuffer], {type: FileTypeMap[format as keyof typeof FileTypeMap]})}
  1. 最重要最核心的音频合成功能:
// 音频合成async mergeAudio(start: number,itemList: TrackItem[],fileName: string,filePath: string) {const { commands } = this.baseCommand.mergeAudio(this.pathConfig,start,itemList)if (this.audioCache.indexOf(commands.join('')) > -1) return falsethis.audioCache = [commands.join('')]if (this.fileExist(filePath, fileName)) this.rmFile(filePath)return this.createTask(commands)}
  1. 获取视频每一帧
 // 获取视频帧图片getFrame(videoName: string, frameIndex: number) {const framePath = `${this.pathConfig.framePath}${videoName}`const fileName = `/pic-${frameIndex}`// return this.getFileBlob(framePath, fileName, 'jpg')return this.getFileBuffer(framePath, fileName, 'jpg')}

目前只是一个简易的demo,如果有需要的话,可以私戳后台,谢谢。

http://www.lryc.cn/news/340209.html

相关文章:

  • stm32实现hid键盘
  • 【单例模式】饿汉式、懒汉式、静态内部类--简单例子
  • windows关闭Windows Search功能
  • 政安晨:【深度学习神经网络基础】(九)—— 在深度学习神经网络反向传播训练中理解梯度
  • 免费的 ChatGPT、GPTs、AI绘画(国内版)
  • UniApp 微信小程序:在 onLaunch 中等待异步方法执行完成后,再调用页面中的接口
  • 【招贤纳士】长期有效
  • 华为配置静态ARP示例
  • LRTimelapse for Mac:专业延时摄影视频制作利器
  • Java复习第十九天学习笔记(Cookie、Session登录),附有道云笔记链接
  • HBase的数据模型与架构
  • 卷积神经网络的结构组成与解释(详细介绍)
  • 使用ansible的连通性检查的关键参数
  • Jenkins用maven风格build报错解决过程记录
  • Web3.0与AI的交融:开启智能互联网新时代
  • 自动化_Ansible学习笔记
  • 用于密集视觉冲击的紧凑三维高斯散射Compact 3D Gaussian Splatting For Dense Visual SLAM
  • ChatGPT揭秘:高效论文写作的秘籍
  • 电脑不能上网,宽带调制解调器出现问题如何处理
  • 云计算: OVN 集群 部署分布式交换机
  • electron+Vue3构建桌面应用之IPC通讯
  • html球体涨水
  • ubuntu手动编译opencv 4.9.0遇到的问题汇总
  • SEO之搜索引擎的工作原理(三)
  • 开发语言漫谈-python
  • JVM指令收集
  • 小程序解析二维码:jsQR
  • 【verilog 设计】 reg有没有必要全部赋初值?
  • NLP问答系统:使用 Deepset SQUAD 和 SQuAD v2 度量评估
  • php开发中如何防止抓包工具伪造请求