vue纯静态实现 视频转GIF 功能(附源码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、实现后的效果
  • 二、使用步骤
    • 1.引入库
    • 2.下载or复制出来js
    • 3. 前端实现
  • 总结


前言

一天一个小demo 今天来一个vue纯静态实现 视频转GIF 功能

上一篇我们讲到了使用html+node.js+sealos实现了一个 多人实时在线聊天的简单小demo ,这次我们来写一个 视频转换成GIF的小功能 使用到的技术为 vue3 + @ffmpeg 很简单。


一、实现后的效果

下面是实现的效果,是我录制视频后转换成GIF的效果,当然下面这个GIF就是转换出来的真实效果哦。
在这里插入图片描述

二、使用步骤

1.引入库

代码如下(初始化一个vue3项目):
我来安装依赖 下面版本号是我现在demo的版本号

 "@ffmpeg/core": "0.12.4","@ffmpeg/ffmpeg": "0.12.7","@ffmpeg/util": "0.12.1",
npm i  @ffmpeg/core@0.12.4
npm i  @ffmpeg/ffmpeg@0.12.7
npm i  @ffmpeg/util@0.12.1

2.下载or复制出来js

我这里把这些给放到public项目里面了

https://unpkg.com/@ffmpeg/core@0.12.4/dist/esm/ffmpeg-core.js
https://unpkg.com/@ffmpeg/core@0.12.4/dist/esm/ffmpeg-core.wasm
https://unpkg.com/@ffmpeg/core@0.12.4/dist/esm/ffmpeg-core.worker.js

在这里插入图片描述

3. 前端实现

<template><div class="video-conversion"><div class="hero-section"><h1>视频转GIF工具</h1><p class="subtitle">简单、快速地将视频转换为高质量GIF动画</p></div><div class="content-wrapper"><div class="upload-section" :class="{ 'has-video': videoUrl }"><div class="upload-area" v-if="!videoUrl" @click="triggerFileInput"><el-icon class="upload-icon"><Upload /></el-icon><p class="upload-text">点击或拖拽视频文件到这里</p><p class="upload-hint">支持 MP4、WebM、MOV 格式 (最大50MB)</p></div><input type="file" @change="handleFileSelect" accept="video/*" ref="fileInput" class="hidden-input"></div><div v-if="videoUrl" class="preview-container"><div class="video-preview"><div class="video-header"><h3>视频预览</h3><el-button type="primary" size="small" @click="triggerFileInput" class="change-video-btn"><el-icon><VideoCamera /></el-icon>更换视频</el-button></div><video :src="videoUrl" controls ref="videoPreview"></video></div><div class="settings-panel"><h3>转换设置</h3><div class="settings-group"><el-form :model="settings" label-position="top"><el-form-item label="GIF宽度"><el-slider v-model="width" :min="100" :max="800" :step="10" show-input /></el-form-item><el-form-item label="帧率 (FPS)"><el-slider v-model="fps" :min="1" :max="30" :step="1" show-input /></el-form-item></el-form></div><el-button type="primary" :loading="isConverting" @click="convertToGif" class="convert-btn">{{ isConverting ? '正在转换...' : '开始转换' }}</el-button></div></div><div v-if="gifUrl" class="result-section"><div class="result-preview"><h3>预览</h3><img :src="gifUrl" alt="转换后的GIF"><div class="action-buttons"><el-button type="success" :href="gifUrl" download="converted.gif" @click="downloadGif">下载GIF</el-button><el-button @click="resetConverter">重新转换</el-button></div></div></div></div><el-alert v-if="error" :title="error" type="error" show-icon class="error-alert" /></div>
</template><script setup>
import { ref, onMounted } from 'vue'
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
import { Upload, VideoCamera } from '@element-plus/icons-vue'const ffmpeg = new FFmpeg()
const loaded = ref(false)
const isConverting = ref(false)
const videoUrl = ref('')
const gifUrl = ref('')
const error = ref('')
const width = ref(480)
const fps = ref(10)
const fileInput = ref(null)const load = async () => {try {const baseURL = window.location.origin;// 修改核心文件的加载方式await ffmpeg.load({coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript')});loaded.value = true;console.log('FFmpeg 加载成功');} catch (err) {console.error('FFmpeg 加载错误:', err);error.value = '加载转换工具失败:' + err.message;}
};// 修改处理文件选择的方法
const handleFileSelect = (event) => {const file = event.target.files[0]if (!file) returnif (file.size > 50 * 1024 * 1024) {error.value = '文件大小不能超过50MB'return}// 如果已经有之前的视频URL,先释放它if (videoUrl.value) {URL.revokeObjectURL(videoUrl.value)}videoUrl.value = URL.createObjectURL(file)gifUrl.value = ''error.value = ''width.value = 480 // 重置设置fps.value = 10
}// 转换为GIF
const convertToGif = async () => {if (!loaded.value) {error.value = '转换工具尚未加载完成'return}try {isConverting.value = trueerror.value = ''// 写入文件到FFmpeg虚拟文件系统const videoFile = await fetch(videoUrl.value)const videoData = await videoFile.arrayBuffer()await ffmpeg.writeFile('input.mp4', new Uint8Array(videoData))// 执行转换命令await ffmpeg.exec(['-i', 'input.mp4','-vf', `scale=${width.value}:-1:flags=lanczos,fps=${fps.value}`,'-c:v', 'gif','output.gif'])// 读取转换后的文件const data = await ffmpeg.readFile('output.gif')const blob = new Blob([data], { type: 'image/gif' })gifUrl.value = URL.createObjectURL(blob)} catch (err) {error.value = '转换失败:' + err.message} finally {isConverting.value = false}
}const triggerFileInput = () => {fileInput.value.click()
}const downloadGif = () => {const link = document.createElement('a')link.href = gifUrl.valuelink.download = 'converted.gif'document.body.appendChild(link)link.click()document.body.removeChild(link)
}const resetConverter = () => {videoUrl.value = ''gifUrl.value = ''error.value = ''width.value = 480fps.value = 10
}onMounted(() => {load()
})
</script><style scoped>
.video-conversion {max-width: 1200px;margin: 0 auto;padding: 40px 20px;color: #1d1d1f;
}.hero-section {text-align: center;margin-bottom: 60px;
}.hero-section h1 {font-size: 48px;font-weight: 600;margin-bottom: 16px;background: linear-gradient(135deg, #1a1a1a 0%, #4a4a4a 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;
}.subtitle {font-size: 24px;color: #86868b;font-weight: 400;
}.content-wrapper {background: white;border-radius: 20px;box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);overflow: hidden;
}.upload-section {padding: 40px;transition: all 0.3s ease;
}.upload-area {border: 2px dashed #d2d2d7;border-radius: 12px;padding: 40px;text-align: center;cursor: pointer;transition: all 0.3s ease;
}.upload-area:hover {border-color: #0071e3;background: rgba(0, 113, 227, 0.05);
}.upload-icon {font-size: 48px;color: #86868b;margin-bottom: 20px;
}.upload-text {font-size: 20px;color: #1d1d1f;margin-bottom: 8px;
}.upload-hint {font-size: 14px;color: #86868b;
}.hidden-input {display: none;
}.preview-container {display: grid;grid-template-columns: 2fr 1fr;gap: 30px;padding: 30px;background: #f5f5f7;
}.video-preview {background: white;padding: 20px;border-radius: 12px;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}.video-preview video {width: 100%;border-radius: 8px;
}.settings-panel {background: white;padding: 30px;border-radius: 12px;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}.settings-panel h3 {font-size: 20px;margin-bottom: 24px;color: #1d1d1f;
}.settings-group {margin-bottom: 30px;
}.convert-btn {width: 100%;height: 44px;font-size: 16px;
}.result-section {padding: 40px;background: white;
}.result-preview {text-align: center;
}.result-preview img {max-width: 100%;border-radius: 12px;margin: 20px 0;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}.action-buttons {display: flex;gap: 16px;justify-content: center;margin-top: 24px;
}.error-alert {position: fixed;top: 20px;right: 20px;z-index: 1000;
}.video-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 15px;
}.video-header h3 {margin: 0;font-size: 18px;color: #1d1d1f;
}.change-video-btn {display: flex;align-items: center;gap: 5px;padding: 8px 15px;font-size: 14px;
}.change-video-btn .el-icon {font-size: 16px;
}.has-video .upload-area {border: 2px solid #d2d2d7;margin-bottom: 20px;
}.has-video .upload-area:hover {border-color: #0071e3;
}@media (max-width: 768px) {.preview-container {grid-template-columns: 1fr;}.hero-section h1 {font-size: 32px;}.subtitle {font-size: 18px;}
}
</style>

总结

以上就是今天要讲的内容,本文仅仅简单介绍了@ffmpeg 库 的使用,而@ffmpeg提供了大量能使我们快速便捷地处理视频数据的函数和方法。

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

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

相关文章

因果机器学习(CausalML)前沿创新思路

结合了传统因果推断与机器学习的因果机器学习是目前AI领域的前沿研究方向&#xff0c;其核心优势在于将因果逻辑融入数据驱动模型&#xff0c;从根本上解决了传统方法的缺陷。因此&#xff0c;它也是突破传统机器学习瓶颈的关键方向&#xff0c;不仅当下热度高&#xff0c;在未…

【CubeMX+STM32】SD卡 U盘文件系统 USB+FATFS

本篇&#xff0c;将使用CubeMXKeil, 创建一个 USBTF卡存储FatFS 的虚拟U盘读写工程。 目录 一、简述 二、CubeMX 配置 SDIO DMA FatFs USB 三、Keil 编辑代码 四、实验效果 串口助手&#xff0c;实现效果&#xff1a; U盘&#xff0c;识别效果&#xff1a; 一、简述 上…

docker nginx 配置文件详解

在平常的开发工作中&#xff0c;我们经常需要访问静态资源&#xff08;图片、HTML页面等&#xff09;、访问文件目录、部署项目时进行负载均衡等。那么我们就会使用到Nginx&#xff0c;nginx.conf 的配置至关重要。那么今天主要结合访问静态资源、负载均衡等总结下 nginx.conf …

解读 Flink Source 接口重构后的 KafkaSource

前言 Apache Kafka 和 Apache Flink 的结合&#xff0c;为构建实时流处理应用提供了一套强大的解决方案[1]。Kafka 作为高吞吐量、低延迟的分布式消息队列&#xff0c;负责数据的采集、缓冲和分发&#xff1b;而 Flink 则是功能强大的流处理引擎&#xff0c;负责对数据进行实时…

【推理llm论文精读】DeepSeek V3技术论文_精工见效果

先附上原始论文和效果对比https://arxiv.org/pdf/2412.19437 摘要 (Abstract) DeepSeek-V3是DeepSeek-AI团队推出的最新力作&#xff0c;一个强大的混合专家&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;语言模型。它拥有671B的总参数量&#xff0c;但每个tok…

如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…

java韩顺平最新教程,Java工程师进阶

简介 HikariCP 是用于创建和管理连接&#xff0c;利用“池”的方式复用连接减少资源开销&#xff0c;和其他数据源一样&#xff0c;也具有连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能&#xff0c;另外&#xff0c;和 druid 一样&#xff0c;HikariCP 也支持监控…

如何在 IDE 里使用 DeepSeek?

近期&#xff0c;阿里云百炼平台重磅推出 DeepSeek-V3、DeepSeek-R1、DeepSeek-R1-Distill-Qwen-32B 等 6 款模型&#xff0c;进一步丰富其 AI 模型矩阵。与此同时&#xff0c;通义灵码也紧跟步伐&#xff0c;全新上线模型选择功能&#xff0c;支持基于百炼的 DeepSeek-V3 和 D…

网络安全技术复习总结

1|0第一章 概论 1.网络安全发展阶段包括四个阶段&#xff1a;通信安全、计算机安全、网络安全、网络空间安全。 2.2017年6月1日&#xff0c;我国第一部全面规范网络空间安全的基础性法律《中华人民共和国网络安全法》正式实施。 3.2021年 6月10日&#xff0c;《中华人民共和…

DedeBIZ系统审计小结

之前简单审计过DedeBIZ系统&#xff0c;网上还没有对这个系统的漏洞有过详尽的分析&#xff0c;于是重新审计并总结文章&#xff0c;记录下自己审计的过程。 https://github.com/DedeBIZ/DedeV6/archive/refs/tags/6.2.10.zip &#x1f4cc;DedeBIZ 系统并非基于 MVC 框架&…

业务开发 | 基础知识 | Maven 快速入门

Maven 快速入门 1.Maven 全面概述 Apache Maven 是一种软件项目管理和理解工具。基于项目对象模型的概念&#xff08;POM&#xff09;&#xff0c;Maven 可以从中央信息中管理项目的构建&#xff0c;报告和文档。 2.Maven 基本功能 因此实际上 Maven 的基本功能就是作为 Ja…

2.11 sqlite3数据库【数据库的相关操作指令、函数】

练习&#xff1a; 将 epoll 服务器 客户端拿来用 客户端&#xff1a;写一个界面&#xff0c;里面有注册登录 服务器&#xff1a;处理注册和登录逻辑&#xff0c;注册的话将注册的账号密码写入数据库&#xff0c;登录的话查询数据库中是否存在账号&#xff0c;并验证密码是否正确…

Python(十九)实现各大跨境船公司物流查询数据处理优化

一、前言 之前已经实现了常用 跨境物流船司 基础信息查询功能&#xff0c;如下所示 实现各大跨境船公司[COSCO/ZIM/MSK/MSC/ONE/PIL]的物流信息查询&#xff1a;https://blog.csdn.net/Makasa/article/details/145484999?spm1001.2014.3001.5501 然后本章在其基础上做了一些…

基于微信小程序的博物馆预约系统的设计与实现

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

深度学习框架TensorFlow怎么用?

大家好呀&#xff0c;以下是使用 TensorFlow 的详细步骤&#xff0c;从安装到构建和训练模型&#xff1a; 一、安装 TensorFlow 安装 Python&#xff1a;TensorFlow 基于 Python&#xff0c;确保已安装 Python&#xff08;推荐 Python 3.8 及以上版本&#xff09;。可通过 Pyt…

如何在华为harmonyOS上调试软件

1、设置-》关于手机-》HarmonyOS 版本连按多下&#xff0c;输入锁屏密码。显示开发者模式已打开。 2、设置-》搜索“开发人员选项”-》开启“开发人员选项”选项。 3、在 开发者选项 中找到 “USB 调试” 并开启。 4、开启 “仅充电时允许 ADB 调试”。 5、设置中开启 &quo…

fpga系列 HDL:Quartus II JTAG 间接配置文件 Indirect Configuration File (.jic) AS模式烧录

先编译生成pof文件 File->Convert Programming Files 转换文件 Tools->Programer 烧录

Python:凯撒密码

题目内容&#xff1a; 凯撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法&#xff0c;它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符&#xff0c;对应关系如下&#xff1a; 原文&#xff1a;A B C D E F G H I J K L M N O P Q R …

【大模型知识点】什么是KV Cache?为什么要使用KV Cache?使用KV Cache会带来什么问题?

1.什么是KV Cache&#xff1f;为什么要使用KV Cache&#xff1f; 理解此问题&#xff0c;首先需理解自注意机制的计算和掩码自注意力机制&#xff0c;在Decoder架构的模型中&#xff0c;每生成一个新的token&#xff0c;便需要重新执行一次自注意力计算&#xff0c;这个过程中…

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式

【STM32】HAL库Host MSC读写外部U盘及FatFS文件系统的USB Disk模式 在先前 分别介绍了FatFS文件系统和USB虚拟U盘MSC配置 前者通过MCU读写Flash建立文件系统 后者通过MSC连接电脑使其能够被操作 这两者可以合起来 就能够实现同时在MCU、USB中操作Flash的文件系统 【STM32】通过…