c#造个轮子--GIF录制工具
在以往几篇文章里面,大家都可以看到各种录制的GIF效果图,把gif放在文章开始,不仅可以减少很多冗余的解释白话文,更可以让读者一览无余看到文章大概要义。
以往都是使用“LicEcap”来录制的,那么我们是否能自己实现一个这样的工具呢?一方面国庆假期结束,练练代码手感,另一方面可以根据自己需求扩展需要的功能。
01介绍软件UI及操作
操作比较简单,以下是运行界面:
-
选择录制区域,绘制需要录制的ROI区域
-
点击开始录制
-
录制结束后,停止录制即可.弹出保存路径保存gif
02效果图
-
整个运行作业图
-
实际录屏的ROI区域效果GIF
03源码介绍
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源码,需要的可以去看看!另外,如果觉得本篇博文对您或者身边朋友有帮助的,麻烦点个关注!赠人玫瑰,手留余香,您的支持就是我写作最大的动力,感谢您的关注,期待和您一起探讨!再会!
暂无评论...