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

news2025/1/30 16:38:00

纯网页版视频编辑器

  • 一、前言
  • 二、功能实现
  • 三、所需技术
  • 四、部分功能实现
    • 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,如果有需要的话,可以私戳后台,谢谢。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

初识数据库与数据库管理系统

实体的概念与数据库 实体(对象): 客观存在的事物都是实体实体数据的存储要求: 必须按照一定的分类和规律存储数据库: 专门用于存储这些实体的信息的数据集合数据库的特点: 海量存储数据&#xff0f;数据检索非常方便保持数据信息的一致&#xff0f;完整&#xff0f;并实现数据…

C# .NET 中的反应式系统

概述&#xff1a;反应式系统已成为构建健壮、可扩展和响应迅速的应用程序的强大范式。这些系统被设计为更具弹性、弹性和消息驱动性&#xff0c;确保它们在各种条件下保持响应&#xff0c;包括高负载、网络延迟和故障。在本文中&#xff0c;我们将探讨 .NET 生态系统中反应式系…

Day 15 Linux网络管理

IP解析 IP地址组成&#xff1a;IP地址由4部分数字组成&#xff0c;每部分数字对应于8位二进制数字&#xff0c;各部分之间用小数点分开&#xff0c;这是点分2进制。如果换算为10进制我们称为点分10进制。 每个ip地址由两部分组成网络地址(NetID)和主机地址(HostID).网络地址表…

DataGrip数据库管理工具安装使用

DataGrip数据库管理工具安装使用 DataGrip介绍 DataGrip是jetbrains旗下的一款数据库管理工具&#xff0c;相信做过java开发的同学都知道&#xff0c;idea就是这家公司发明的。 DataGrip 是JetBrains公司开发的数据库管理客户端工具&#xff08;操作数据库的IDE&#xff0c;…

看图找LOGO,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建生活场景下的商品商标LOGO检测识别系统

日常生活中&#xff0c;我们会看到眼花缭乱的各种各样的产品logo&#xff0c;但是往往却未必能认全&#xff0c;正因为有这个想法&#xff0c;这里我花费了过去近两周的时间采集和构建了包含50种商品商标logo的数据集&#xff0c;基于YOLOv8全系列的参数模型开发构建了对应的检…

初识--Linux的虚拟地址空间

重新了解地址空间 在学习c/c语言的时候,大家一定见过以下这张图 说的是程序会加载在如图的结构上,实际上,我们真的对他很了解吗,而在Linux进程控制这,就会有一个奇怪的现象 前提提要:简要介绍一下fork函数 进程内核数据结构(PCB)自己的代码以及数据 在Linux中,fork可以从当…

什么是邮箱分身?如何快速创建30个邮箱分身?

很多人只知道微信、QQ等应用分身&#xff0c;对于邮箱分身并不是很了解。邮箱分身和他们的不同点在于我们直接在原有邮箱的基础上创立新的虚拟邮箱地址&#xff0c;并且密码一致&#xff0c;在我们需要运营多个社交媒体账号或者管理多个项目的情况下&#xff0c;邮箱分身是一个…

盲盒小程序成为收益“法宝”?盲盒线上如何发展

近年来&#xff0c;盲盒在年轻人中掀起了一股潮玩热风&#xff0c;受到了不少年轻人的青睐&#xff0c;盲盒商品更是在不断创新中&#xff0c;收藏价值逐渐提高。随着市场规模的扩大&#xff0c;越来越多的玩家和商家涌入到了市场中&#xff0c;盲盒的商业模式正在加快发展中。…

人工智能与IP代理池:解析网络数据采集的未来

前言 随着互联网的快速发展&#xff0c;数据成为了当今社会最宝贵的资源之一。然而&#xff0c;要获取大量的网络数据并进行有效的分析&#xff0c;往往需要面对诸多挑战&#xff0c;其中之一就是网络封锁与反爬虫机制。在这个背景下&#xff0c;人工智能&#xff08;AI&#x…

【CANN训练营】目标检测(YoloV5s)实践(Python实现)

样例介绍 使用多路离线视频流&#xff08;* .mp4&#xff09;作为应用程序的输入&#xff0c;基于YoloV5s模型对输入视频中的物体做实时检测&#xff0c;将推理结果信息使用imshow方式显示。 样例代码逻辑如下所示&#xff1a; 环境信息 CPU&#xff1a;Intel Xeon Gold 63…

ASP.NET基于CS应用程序平台多语种技术应用研究

摘 要 C/S应用程序平台多语种技术是一种基于C/S应用技术结构平台的关于多语种的转换和翻译技术。本设计基于Visual Studio.Net集成开发环境&#xff0c;采用SQL Server2000进行数据库后台开发。通过采用数据字典实现应用系统的静态文本转换&#xff1b;通过使用Visual Studio.…

适用于 Windows 的 10 个顶级 PDF 编辑器 [免费和付费]

曾经打开PDF文件&#xff0c;感觉自己被困在数字迷宫中吗&#xff1f;无法编辑的文本、无法调整大小的图像以及签署感觉像是一件苦差事的文档&#xff1f;好吧&#xff0c;不用再担心了&#xff01;本指南解开了在 Windows 上掌握 PDF 的秘密&#xff0c;其中包含 10 款适用于 …

LoRA:大模型的低阶自适用(使用BERT在IMDB数据集上运用LoRA微调)

文章目录 简介LoRA文章主要贡献LoRA技术模型图技术细节论文实验结果LoRA在bert的运用LoRA核心代码实战分析 简介 论文链接https://arxiv.org/pdf/2106.09685v2.pdf 本文将先介绍论文中的LoRA技术&#xff0c;然后以BERT为例在IMDB数据集上代码实现运用这项微调技术。 代码数…

OpenCV基本图像处理操作(四)——傅立叶变换

傅里叶变换的作用 高频&#xff1a;变化剧烈的灰度分量&#xff0c;例如边界 低频&#xff1a;变化缓慢的灰度分量&#xff0c;例如一片大海 滤波 低通滤波器&#xff1a;只保留低频&#xff0c;会使得图像模糊 高通滤波器&#xff1a;只保留高频&#xff0c;会使得图像细节…

【React】Ant Design自定义主题风格及主题切换

Ant Design 的自定义主题&#xff0c;对于刚入手的时候感觉真是一脸蒙圈&#xff0c;那今天给它梳理倒腾下&#xff1b; 1、自定义主题要点 整体样式变化&#xff0c;主要两个部分&#xff1a; 1.1、Design Token https://ant.design/docs/react/customize-theme-cn#theme 官…

新经济助推高质量发展“大有云钞”聚焦未来趋势

近日&#xff0c;由大有云钞科技&#xff08;北京&#xff09;有限公司主办的一场关于“新经济助力高质量发展法治研讨会”在北京国家会议中心隆重举行。此次研讨会汇聚了来自政府、企业、学术界和法律界的众多专家学者&#xff0c;共同探讨新经济背景下的法治建设和高质量发展…

0基础如何入门编程?

0基础如何进入IT行业 &#xff1f; 前言 简介&#xff1a;对于没有任何相关背景知识的人来说&#xff0c;如何才能成功进入IT行业&#xff1f;是否有一些特定的方法或技巧可以帮助他们实现这一目标&#xff1f; 主要方法有如下几点建议提供给宝子们 目录 免费视频网课学习…

static+单例模式+类的复合继承

汇编语言 汇编语言是最靠谱的验证“编程语言相关知识点”正确性的方式 汇编语言与机器语言一一对应&#xff0c;每一条机器语言都有与之对应的汇编指令 机器语言是计算机使用的语言&#xff0c;它是一串二进制数字 汇编语言可以通过汇编得到机器语言机器语言可以通过反汇编得到…

Shell循环以及条件语句使用

Shell脚本基础已经发过&#xff0c;可在主页查找&#xff0c;现在讲解case&#xff0c;for&#xff0c;while语句&#xff0c;以及语句的练习。 1.case语句 等同于C语⾔的switch-case 格式&#xff1a; case $变量 in # 判断变量的值 a) # 值是什么语句;; # 相当于break 但…

docker网路和主机通讯问题

#注 1&#xff0c;安装docker和启动容器服务的时候如果防火墙处于开启状态&#xff0c;那么重启docker里面的容器的时候必须开启防火墙&#xff0c;否则会出现iptable错误&#xff1b; 2&#xff0c;linux开启防火墙会导致主机和docker网络之间单向通讯&#xff0c;主机可以访…