news 2026/2/25 9:22:17

c#造个轮子--GIF录制工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
c#造个轮子--GIF录制工具

在以往几篇文章里面,大家都可以看到各种录制的GIF效果图,把gif放在文章开始,不仅可以减少很多冗余的解释白话文,更可以让读者一览无余看到文章大概要义。

以往都是使用“LicEcap”来录制的,那么我们是否能自己实现一个这样的工具呢?一方面国庆假期结束,练练代码手感,另一方面可以根据自己需求扩展需要的功能。

01介绍软件UI及操作

操作比较简单,以下是运行界面:

选择录制区域,绘制需要录制的ROI区域

点击开始录制

录制结束后,停止录制即可.弹出保存路径保存gif

image.png

02效果图

整个运行作业图

test.gif

实际录屏的ROI区域效果GIF

eee.gif

03源码介绍

image

private void InitializeComponents()

{

this.Text = "GIF录制工具";

this.Size = new Size(400, 200);

this.StartPosition = FormStartPosition.CenterScreen;

// 选择区域按钮

Button btnSelectArea = new Button();

btnSelectArea.Text = "选择录制区域";

btnSelectArea.Size = new Size(120, 30);

btnSelectArea.Location = new Point(20, 20);

btnSelectArea.Click += BtnSelectArea_Click;

this.Controls.Add(btnSelectArea);

// 开始录制按钮

Button btnStart = new Button();

btnStart.Text = "开始录制";

btnStart.Size = new Size(120, 30);

btnStart.Location = new Point(20, 60);

btnStart.Click += BtnStart_Click;

this.Controls.Add(btnStart);

// 停止录制按钮

Button btnStop = new Button();

btnStop.Text = "停止录制";

btnStop.Size = new Size(120, 30);

btnStop.Location = new Point(20, 100);

btnStop.Click += BtnStop_Click;

this.Controls.Add(btnStop);

// 帧率选择

Label lblFrameRate = new Label();

lblFrameRate.Text = "帧率:";

lblFrameRate.Location = new Point(160, 65);

lblFrameRate.Size = new Size(50, 20);

this.Controls.Add(lblFrameRate);

NumericUpDown numFrameRate = new NumericUpDown();

numFrameRate.Value = frameRate;

numFrameRate.Minimum = 1;

numFrameRate.Maximum = 30;

numFrameRate.Location = new Point(210, 65);

numFrameRate.Size = new Size(60, 20);

numFrameRate.ValueChanged += (s, e) => { frameRate = (int)numFrameRate.Value; };

this.Controls.Add(numFrameRate);

// 状态标签

Label lblStatus = new Label();

lblStatus.Text = "状态: 就绪";

lblStatus.Location = new Point(160, 25);

lblStatus.Size = new Size(200, 20);

lblStatus.Name = "lblStatus";

this.Controls.Add(lblStatus);

// 录制计时器

captureTimer = new System.Windows.Forms.Timer();

captureTimer.Tick += CaptureTimer_Tick;

}

选择ROI录屏区域

private void StartAreaSelection()

{

this.Hide();

Thread.Sleep(500); // 等待窗体隐藏

isSelectingArea = true;

Cursor = Cursors.Cross;

// 创建全屏透明窗体用于区域选择

Form selectionForm = new Form();

selectionForm.WindowState = FormWindowState.Maximized;

selectionForm.FormBorderStyle = FormBorderStyle.None;

selectionForm.BackColor = Color.Black;

selectionForm.Opacity = 0.3;

selectionForm.TopMost = true;

selectionForm.Cursor = Cursors.Cross;

Rectangle selectedArea = Rectangle.Empty;

bool isDragging = false;

Point dragStart = Point.Empty;

selectionForm.MouseDown += (s, e) =>

{

if (e.Button == MouseButtons.Left)

{

isDragging = true;

dragStart = e.Location;

}

};

selectionForm.MouseMove += (s, e) =>

{

if (isDragging)

{

int x = Math.Min(dragStart.X, e.X);

int y = Math.Min(dragStart.Y, e.Y);

int width = Math.Abs(e.X - dragStart.X);

int height = Math.Abs(e.Y - dragStart.Y);

selectedArea = new Rectangle(x, y, width, height);

selectionForm.Invalidate();

}

};

selectionForm.MouseUp += (s, e) =>

{

if (e.Button == MouseButtons.Left && isDragging)

{

isDragging = false;

if (selectedArea.Width > 10 && selectedArea.Height > 10)

{

recordingArea = selectedArea;

UpdateStatus($"已选择区域: {recordingArea}");

}

selectionForm.Close();

}

};

selectionForm.Paint += (s, e) =>

{

if (isDragging && !selectedArea.IsEmpty)

{

using (Pen pen = new Pen(Color.Red, 2))

{

e.Graphics.DrawRectangle(pen, selectedArea);

}

string sizeText = $"{selectedArea.Width} x {selectedArea.Height}";

using (Font font = new Font("Arial", 12))

using (Brush brush = new SolidBrush(Color.Red))

{

e.Graphics.DrawString(sizeText, font, brush, selectedArea.X, selectedArea.Y - 20);

}

}

};

selectionForm.KeyDown += (s, e) =>

{

if (e.KeyCode == Keys.Escape)

{

selectionForm.Close();

}

};

selectionForm.FormClosed += (s, e) =>

{

isSelectingArea = false;

Cursor = Cursors.Default;

this.Show();

this.BringToFront();

};

selectionForm.ShowDialog();

}

录制结束,保存GIF

private void SaveGif()

{

if (frames.Count == 0)

{

MessageBox.Show("没有可保存的帧!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

}

using (SaveFileDialog saveDialog = new SaveFileDialog())

{

saveDialog.Filter = "GIF 文件|*.gif";

saveDialog.Title = "保存GIF文件";

saveDialog.DefaultExt = "gif";

if (saveDialog.ShowDialog() == DialogResult.OK)

{

try

{

// 使用GifBitmapEncoder替代方案

SaveFramesAsGif(frames, saveDialog.FileName, frameRate);

MessageBox.Show($"GIF保存成功!\n文件: {saveDialog.FileName}\n帧数: {frames.Count}", "成功",

MessageBoxButtons.OK, MessageBoxIcon.Information);

}

catch (Exception ex)

{

MessageBox.Show($"保存GIF时出错: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

}

}

// 清理资源

foreach (var frame in frames)

{

frame.Dispose();

}

frames.Clear();

}

private void SaveFramesAsGif(List<Bitmap> frames, string filePath, int frameRate)

{

using (var collection = new MagickImageCollection())

{

foreach (var frame in frames)

{

using (var memoryStream = new MemoryStream())

{

frame.Save(memoryStream, ImageFormat.Bmp);

memoryStream.Position = 0;

var image = new MagickImage(memoryStream);

image.AnimationDelay =Convert.ToUInt32( 100 / frameRate); // 设置帧延迟

collection.Add(image);

}

}

// 优化GIF

collection.Optimize();

collection.Write(filePath);

}

}

主要用到第三方nuget包

AnimatedGif

Magick.NET-Q16-AnyCPU

结束语

感谢各位耐心查阅! 如果您有更好的想法欢迎一起交流,有不懂的也可以微信公众号联系博主,作者公众号会经常发一些实用的小工具和demo源码,需要的可以去看看!另外,如果觉得本篇博文对您或者身边朋友有帮助的,麻烦点

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/22 16:01:10

专利申请怕驳回、分类难?别担心!星河智源申请前评估来帮你

在科技创新驱动发展的当下&#xff0c;专利是保护创新成果、彰显核心竞争力的关键。无论是科研人员的成果转化&#xff0c;还是企业的市场布局&#xff0c;专利都发挥着不可替代的作用。例如华为凭借海量5G专利领跑全球通信市场&#xff0c;足以证明优质专利的核心价值。 但专…

作者头像 李华
网站建设 2026/2/25 3:13:04

【EF Core】通过 DbContext 选项扩展框架

本来老周计划在 10 月 1 日或 2 日写这篇水文的&#xff0c;没打算出去玩&#xff08;确实没啥好玩&#xff09;。不过因为买的运动相机到手&#xff0c;急着想试试效果&#xff0c;于是就备了些干粮&#xff0c;骑着山地车在外面鬼混了一天。10 月 2 日&#xff0c;家里来了三…

作者头像 李华
网站建设 2026/2/22 22:47:20

新用户免费试用EmotiVoice 1000个token

EmotiVoice&#xff1a;用1000个免费Token开启高表现力语音合成之旅 在虚拟主播的直播间里&#xff0c;一句“太开心了&#xff01;”如果只是平平无奇地念出来&#xff0c;观众很难被感染&#xff1b;而在智能助手中&#xff0c;当用户情绪低落时&#xff0c;机械冷漠的回应只…

作者头像 李华
网站建设 2026/2/24 9:56:02

免费视频增强神器:3步将模糊视频升级4K超清画质

免费视频增强神器&#xff1a;3步将模糊视频升级4K超清画质 【免费下载链接】SeedVR-7B 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/SeedVR-7B 想要让那些模糊的家庭录像、珍贵回忆重获新生吗&#xff1f;字节跳动SeedVR视频增强工具为你带来专业级的…

作者头像 李华
网站建设 2026/2/20 21:51:54

dp 总结 1

shout out to professor Adzlpxsn.upd at oct 16th 2025, 修复了时间复杂度分析的重大失误.基本的, 状态, 转移, 方程状态一句话概况即为当前的属性.比如说, 贝贝现在是 3030 岁, 发了 00 张专辑, 我们就可以说 &#xfffd;300f 30​0.这里我们说 3030 和 00 是不同的信息, 所…

作者头像 李华
网站建设 2026/2/13 16:08:40

5大核心参数精准调优:从理论到实践的Faiss HNSW索引优化指南

5大核心参数精准调优&#xff1a;从理论到实践的Faiss HNSW索引优化指南 【免费下载链接】faiss A library for efficient similarity search and clustering of dense vectors. 项目地址: https://gitcode.com/GitHub_Trending/fa/faiss 面对海量向量数据的检索挑战&am…

作者头像 李华