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

纯网页版视频编辑器

  • 一、前言
  • 二、功能实现
  • 三、所需技术
  • 四、部分功能实现
    • 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.type
      let 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: FFmpeg
  private taskQueue = reactive<Task[]>([]) // 任务队列
  private running = ref(false) // 运行状态
  public showLog = true
  public 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. 用户把素材资源从本地拖拽到页面内,需要获取到文件内容
 // 获取文件Blob
  getFileBlob(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 false
    this.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,如果有需要的话,可以私戳后台,谢谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/548456.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KITTI结果领先地位!Progressive LiDAR Adaptation for Road Detection——PLARD算法

描述 详解一篇基于激光视觉融合的道路检测文章&#xff0c;发表在2019年自动化学报英文版&#xff08;我所主编的业界顶刊&#xff09;中&#xff0c;第三作者是陶大程&#xff0c;业界大佬&#xff0c;可自行进行百度。 为什么选择这篇文章进行分析呢。查看KITTI数据集的分数…

分布式数据库Polardb-X架构及特点

PolarDB-X架构 计算节点&#xff08;Compute Node&#xff0c;CN&#xff09;是系统的入口&#xff0c;采用无状态设计的sql引擎提供分布式路由和计算&#xff0c;包括SQL解析器、优化器、执行器等模块。负责数据分布式路由、计算及动态调度&#xff0c;负责分布式事务2PC协调…

CFDPro雾化仿真 | 专为雾化过程与液滴属性研究设计的仿真模块

雾化是一种将液体转化为微小液滴的技术&#xff0c;通过不同的雾化方法实现液体的高效分散、蒸发、燃烧、吸附或沉积等目的。 雾化仿真在多个工业领域中具有极其重要的地位。无论是内燃机中燃油的高效燃烧&#xff0c;还是化工生产中的喷雾干燥&#xff0c;以及农业喷雾中农药…

[linux]进程控制——进程终止

一、main函数的返回值 我们在编写C语言的程序时&#xff0c;通常会这样写&#xff1a; int main() {return 0; } 那么我们为什么要返回&#xff08;return&#xff09;0 呢&#xff1f; 其实&#xff0c;main函数也是一个函数&#xff0c;它也会被调用&#xff0c;所以谁调…

【力扣 Hot100 | 第四天】4.15(括号生成)

文章目录 4.括号生成4.1题目4.2解法&#xff1a;回溯4.2.1回溯思路&#xff08;1&#xff09;函数返回值以及参数&#xff08;2&#xff09;终止条件&#xff08;3&#xff09;遍历过程 4.2.2代码 4.括号生成 4.1题目 数字 n 代表生成括号的对数&#xff0c;请你设计一个函数…

计算机笔记(11)续20个

180&#xff0e;时钟频率2.0GHz表示一秒有2*10的9次方个时钟周期&#xff0c;若执行一条指令需要2个时钟周期&#xff0c;则每秒执行的指令数为2*10的9次方/21*10的9次方 181.同轴电缆粗缆采用AUI头作为连接器件 182. 183.win7中的回收站&#xff0c;存放的是硬盘上被删除的…

C语言:文件操作(三)

目录 前言 5、文章的随机读写 5.1 fseek 5.2 ftell 5.3 rewind 结语 前言 本篇文章继续讲解文件操作&#xff0c;讲解文件的随机读写&#xff0c;主要有三个函数&#xff1a;fseek&#xff1b;ftell&#xff1b;rewind。 前面讲解的函数都是对文件内容进行顺序读写&#x…

MySQL 8.0.19安装教程(windows 64位)

在c盘目录下的Program Files目录下创建MySQL目录&#xff0c;将下载好的mysql解压到里面 解压完是这个样子 配置初始化的my.ini文件的文件 [mysqld] # 设置3306端口 port3306 # 设置mysql的安装目录 basedirC:\Program Files\MySQL # 设置mysql数据库的数据的存放目录 datad…

Trl SFT: llama2-7b-hf使用QLora 4bit量化后ds zero3加上flash atten v2单机多卡训练(笔记)

目录 一、环境 1.1、环境安装 1.2、安装flash atten 二、代码 2.1、bash脚本 2.2、utils.py 注释与优化 2.3、train.py 注释与优化 2.4、模型/参数相关 2.4.1、量化后的模型 2.4.1.1 量化后模型结构 2.4.1.2 量化后模型layers 2.4.2、参数 2.4.2.1 training args 2.4.2.2 pe…

【随笔】Git 基础篇 -- 拉取数据 git pull(二十八)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

GPT的使用

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 网站sms-activate.or…

详解电源测试系统自定义报告模板功能:如何轻松实现数据导出

在NSAT-8000电源测试系统内&#xff0c;数据一般分为三级架构&#xff1a;原始数据、数据报告和数据分析。数据报告可以直接展示出电源模块的各项测试数据和测试结果&#xff0c;帮助用户评估电源性能&#xff0c;为电源的优化提升提供数据支持。 系统的记录报告板块展示着历史…

RocketMQ 10 面试题FAQ

RocketMQ 面试FAQ 说说你们公司线上生产环境用的是什么消息中间件? 为什么要使用MQ&#xff1f; 因为项目比较大&#xff0c;做了分布式系统&#xff0c;所有远程服务调用请求都是同步执行经常出问题&#xff0c;所以引入了mq 解耦 系统耦合度降低&#xff0c;没有强依赖…

DeiT:训练ImageNet仅用4卡不到3天的平民ViT | ICML 2021

论文基于改进训练配置以及一种新颖的蒸馏方式&#xff0c;提出了仅用ImageNet就能训练出来的Transformer网络DeiT。在蒸馏学习时&#xff0c;DeiT以卷积网络作为teacher&#xff0c;能够结合当前主流的数据增强和训练策略来进一步提高性能。从实验结果来看&#xff0c;效果很不…

基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持PXI…

html基础——CSS

在HTML中&#xff0c;CSS的作用是用于控制网页的样式&#xff0c;包括字体、颜色、背景、布局等方面的设计。通过一个样例来说明CSS的作用&#xff1a; 如下是一个名为global.css的CSS文件&#xff1a; .C1{font-size: 10px;color: blue;border:1px solid red;height: 200px;…

【Redis 神秘大陆】009 案例实践进阶

九、案例实践&进阶方案 9.1 本地缓存组件选型 使用缓存组件时需要重点关注集群方式、集群、缓存命中率。 需要关注集群组建方式、缓存统计&#xff1b;还需要考虑缓存开发语言对缓存的影响&#xff0c;如对于JAVA开发的缓存需要考虑GC的影响&#xff1b;最后还要特别关注…

vue快速入门(二十六)生命周期钩子函数

注释很详细&#xff0c;直接上代码 上一篇 新增内容 生命周期钩子函数的解析生命周期函数效果演示 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevic…

【原创教程】海为PLC与RS-WS-ETH-6传感器的MUDBUS_TCP通讯

一、关于RS-WS-ETH-6传感器的准备工作 要完成MODBUS_TCP通讯,我们必须要知道设备的IP地址如何分配,只有PLC和设备的IP在同一网段上,才能建立通讯。然后还要选择TCP的工作模式,来建立设备端和PC端的端口号。接下来了解设备的报文格式,方便之后发送报文完成数据交互。 1、…

【Altium Designer 20 笔记】PCB层

Top Overlay & Bottom Overlay (顶部丝印层和底部丝印层)&#xff1a; 用于标记元件、连接和其他重要信息。丝印层是 PCB 表面的一层&#xff0c;上面印上文字、图标或标记。 Top Solder & Bottom Solder (顶部阻焊层和底部阻焊层)&#xff1a; 阻焊层、开窗层、绿油层…