简介:直接对WPF界面中的任意UIElement(如Panel、UserControl、RoundedCornerGenerator、ColorPicker等自定义控件)进行像素级截图并保存为PNG文件。底层基于RenderTargetBitmap实现,完整保留Alpha通道,支持透明背景输出;可自由设置导出尺寸、缩放比例,并自动适配系统DPI缩放设置。项目已集成配置管理,通过Settings.settings持久化存储常用参数(如默认导出路径、宽高偏好、是否启用缩放等),Resources.resx预留多语言界面扩展能力。工程结构清晰,已排除bin/obj目录,开箱即用:加载WPFImages.sln后,调用ExportToPng方法传入目标元素即可完成导出。适用于UI设计稿归档、自动化测试截图比对、原型评审素材准备、文档插图生成等实际开发场景。
1. 项目概述:为什么一个“能截图的WPF工具”值得单独写一篇深度实操笔记?
在WPF开发一线干了十多年,我经手过上百个UI组件库、设计系统和自动化测试平台。每次遇到“把某个控件截图存档”这个需求,团队里总有人下意识打开Snipaste、用PrintScreen截窗口、甚至手动调RenderTargetBitmap写临时代码——结果不是背景变黑、圆角锯齿、文字发虚,就是高分屏上导出图尺寸错乱、DPI缩放后像素糊成一片。直到去年给一个医疗影像标注系统的UI做自动化回归测试时,我们被这个问题卡了整整三天:ColorPicker控件在200% DPI下导出的PNG,色块边界全是半透明灰边;RoundedCornerGenerator生成的圆角矩形,导出后右下角总有一像素的直角残留。最后发现,根本不是代码逻辑错了,而是没真正理解RenderTargetBitmap在WPF渲染管线中的真实定位,也没吃透WPF的DPI感知机制与位图渲染的耦合关系。
这个工具,就是我把那三天踩过的所有坑、翻过的所有源码、验证过的每一种DPI适配路径,浓缩成的一个可复用、可调试、可嵌入任何WPF项目的轻量级截图模块。它不叫“截图软件”,而是一个UI元素像素级快照生成器——核心就一句话:给你一个UIElement,还你一张原生、保真、带Alpha通道、尺寸可控、DPI自适应的PNG文件。关键词里的“WPF截图工具”是表象,“PNG导出”是交付物,“UI元素渲染”才是本质。它解决的从来不是“怎么截屏”,而是“如何让WPF的视觉树,在脱离窗口上下文后,依然能按预期完成一次完整、干净、可预测的光栅化渲染”。
它适合三类人直接抄作业:一是UI组件库作者,需要为每个自定义控件(比如你写的RoundedCornerGenerator或ColorPicker)批量生成高清文档图;二是自动化测试工程师,要在CI流水线里对关键界面节点做像素级比对;三是原型设计师,想把正在调试的交互面板一键导出为设计稿插图,不用切到Sketch再手动抠图。它不依赖外部进程、不触发窗口重绘、不模拟鼠标点击,所有操作都在内存中完成——这才是WPF原生能力该有的样子。
2. 核心原理拆解:RenderTargetBitmap不是“截图”,而是“离屏渲染快照”
很多人一看到“截图”,第一反应就是CopyFromScreen或者Graphics.CopyFromScreen,这是WinForms时代的惯性思维。但在WPF里,RenderTargetBitmap根本不是在“抓取屏幕”,而是在请求WPF渲染引擎,为指定的Visual对象执行一次独立的、离屏的、可配置的光栅化渲染流程。理解这一点,是避免90%诡异问题的前提。
2.1 渲染管线中的RenderTargetBitmap定位
WPF的渲染分两层:上层是基于Visual树的矢量描述(XAML/代码构建的逻辑结构),下层是Composition Engine驱动的GPU加速光栅化(最终画到显存)。RenderTargetBitmap介入的位置,是在Visual树被转换为DrawingVisual之后、进入GPU光栅化之前。它相当于在渲染管线中“插了一个探针”,把本该送给显卡的绘制指令,临时重定向到一块内存位图缓冲区里。
提示:
RenderTargetBitmap接收的是Visual,不是UIElement。所以当你传入一个Button或Grid时,WPF内部会先调用UIElement.GetVisualChild(0)获取其底层Visual,再交给渲染器。这意味着:如果控件重写了GetVisualChild或禁用了视觉树(如设置了IsHitTestVisible=false且未显式处理),RenderTargetBitmap可能捕获不到内容——这不是Bug,是WPF渲染模型的必然结果。
2.2 DPI适配的本质:不是“缩放图片”,而是“调整渲染分辨率”
WPF的DPI适配常被误解为“把图片放大2倍”。实际机制更底层:当系统DPI设置为200%时,WPF会自动将VisualTree的RenderTransform应用一个ScaleTransform(2,2),同时将RenderTargetBitmap的PixelWidth和PixelHeight参数也按比例放大。但这里有个关键陷阱:RenderTargetBitmap构造函数的宽高参数,单位是设备无关像素(DIP),而非物理像素。如果你直接传入控件的ActualWidth/ActualHeight(它们返回的是DIP值),在200% DPI下,RenderTargetBitmap会按2倍物理像素渲染,但输出的PNG元数据里DPI信息仍是96,导致图片在Photoshop里打开时显示尺寸缩小一半。
解决方案是:显式计算目标物理像素尺寸,并同步设置PNG编码器的DPI元数据。公式如下:
double dpiScale = VisualTreeHelper.GetDpi(targetElement).PixelsPerInchX / 96.0;
int physicalWidth = (int)Math.Ceiling(targetElement.ActualWidth * dpiScale);
int physicalHeight = (int)Math.Ceiling(targetElement.ActualHeight * dpiScale);
然后创建RenderTargetBitmap时传入physicalWidth和physicalHeight,并在保存PNG时通过PngBitmapEncoder的Metadata属性写入正确的DPI值(96 * dpiScale)。这样导出的PNG在任何查看器里,物理尺寸都与原始控件在屏幕上显示的一致。
2.3 透明背景的实现逻辑:Alpha通道必须全程保留
WPF默认渲染背景是黑色(SolidColorBrush.Black),所以如果不对目标元素预设背景,RenderTargetBitmap会渲染出黑底。但“透明背景”不是简单地把背景设为Transparent——因为Transparent在WPF中本质是#00FFFFFF(Alpha=0),而RenderTargetBitmap在渲染时若检测到目标区域无内容,会填充默认背景色(黑)。真正的做法是:在渲染前,强制将RenderTargetBitmap的Clear操作指向全透明色,并确保目标元素的Background属性为null或Brushes.Transparent。
更关键的是编码环节:PngBitmapEncoder默认会丢弃Alpha通道(除非显式启用)。必须设置:
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
encoder.PngMetadata = new PngMetadata { ... }; // 设置DPI
// 且必须确保BitmapSource的PixelFormat是Bgra32(带Alpha)
否则即使渲染时是透明的,保存后PNG也会变成RGB格式,背景变黑。
3. 工程结构与关键实现:从Settings.settings到ExportToPng方法的完整链路
整个项目命名为WPFImages,结构极简但精准。没有花哨的MVVM框架,所有逻辑集中在ExportService.cs一个文件里,符合“工具类”定位。下面拆解从配置加载到文件落地的每一环。
3.1 配置管理:Settings.settings不只是存路径,更是DPI策略开关
Properties\Settings.settings里定义了5个关键配置项:
- DefaultExportPath(string):默认导出目录,支持相对路径(如.\Exports)和环境变量(如%USERPROFILE%\Documents\WPFExports)。
- DefaultWidth/DefaultHeight(int):用户偏好的导出宽度/高度(单位:DIP)。若为0,则按控件实际尺寸导出。
- EnableScaling(bool):是否启用自定义缩放。若为true,则忽略DefaultWidth/Height,使用ScaleFactor。
- ScaleFactor(double):缩放系数(如1.5表示150%放大)。注意:此缩放作用于DIP尺寸,非物理像素。
注意:
EnableScaling和DefaultWidth/Height是互斥策略。代码中通过if (Settings.Default.EnableScaling)分支控制,避免参数冲突。我在调试时曾因两者同时启用,导致导出图尺寸被缩放两次——第一次按ScaleFactor放大DIP,第二次又按DPI缩放转物理像素,最终图片大得离谱。现在逻辑已加固:启用缩放时,DefaultWidth/Height仅作为最小尺寸约束(防止缩放后小于1像素)。
3.2 多语言扩展:Resources.resx的预留接口与实际价值
Properties\Resources.resx目前只包含一句ExportSuccessMessage(“导出成功:{0}”),但它的结构已为多语言铺平道路。关键点在于:所有UI字符串(如按钮文本、状态栏提示)都应从此资源读取,而非硬编码。例如导出按钮的Content绑定:
<Button Content="{x:Static p:Resources.ExportButton}" Click="OnExportClick"/>
其中p是Properties命名空间映射。这样,只需添加Resources.zh-CN.resx、Resources.ja-JP.resx等文件,编译时自动打包对应卫星程序集。实际项目中,我们为海外客户交付时,仅用3小时就完成了日语、德语版本切换——因为所有字符串提取工作,在最初设计时就完成了。
3.3 ExportToPng方法:12行核心代码背后的7层校验
ExportService.ExportToPng(UIElement element, string filePath)是唯一对外API。表面看只有12行,但背后有7层防御性检查:
- 空引用检查:
element == null抛ArgumentNullException,附带nameof(element)提示。 - 可视化状态检查:
!element.IsLoaded || element.Visibility != Visibility.Visible时警告并返回false(未加载的控件无法渲染)。 - 尺寸有效性检查:
element.ActualWidth <= 0 || element.ActualHeight <= 0时尝试UpdateLayout()并重试一次,避免布局未完成导致尺寸为0。 - DPI获取校验:
VisualTreeHelper.GetDpi(element)返回(96,96)时,强制回退到PresentationSource.FromVisual(element)?.CompositionTarget?.TransformToDevice计算,兼容老旧WPF版本。 - 路径合法性检查:
Path.GetDirectoryName(filePath)不存在时自动创建;filePath含非法字符则抛ArgumentException并给出具体非法字符列表。 - 渲染尺寸裁剪:物理像素宽高上限设为
32767(GDI+限制),超限时按比例缩放并记录警告日志。 - 文件写入原子性:使用
FileStream以FileMode.CreateNew打开,避免覆盖已有文件;失败时自动清理临时文件。
实操心得:第3步“尺寸有效性检查”是我加的最实用的补丁。有次测试
RoundedCornerGenerator时,它在Loaded事件里才动态设置Width,导致ExportToPng调用时ActualWidth为0。现在方法会主动触发element.UpdateLayout()并等待SizeChanged事件(带超时),确保拿到真实尺寸。这比让用户自己写Dispatcher.InvokeAsync可靠得多。
3.4 RoundedCornerGenerator.xaml.cs:自定义控件的截图适配实践
RoundedCornerGenerator是个典型例子:它通过Geometry绘制圆角矩形,不依赖Border.CornerRadius。截图时常见问题是圆角边缘出现1像素锯齿。根源在于:RenderTargetBitmap默认使用BitmapCacheOption.OnLoad,而Geometry渲染依赖抗锯齿,但离屏渲染时若未显式开启UseLayoutRounding,亚像素坐标会被截断。
解决方案在RoundedCornerGenerator的构造函数中加入:
public RoundedCornerGenerator()
{
InitializeComponent();
// 强制启用布局舍入,确保圆角坐标对齐像素网格
UseLayoutRounding = true;
// 启用文本清晰度(对含文字的圆角控件有效)
TextOptions.SetTextRenderingMode(this, TextRenderingMode.ClearType);
}
同时,在ExportToPng中渲染前,临时设置:
// 临时提升渲染质量
element.SetValue(RenderOptions.BitmapScalingModeProperty, BitmapScalingMode.HighQuality);
element.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased); // 对几何图形更锐利
实测下来,圆角边缘的锯齿完全消失,与屏幕显示一致。
4. 实操全流程:从零开始导出一张ColorPicker的PNG
假设你现在要为ColorPicker控件生成一张高清文档图。以下是完整、可复现的操作链,每一步都标注了原理和避坑点。
4.1 环境准备:确认DPI设置与WPF项目兼容性
首先,在Windows设置中查看当前DPI缩放比例(设置 > 系统 > 显示 > 缩放与布局)。假设为150%。然后启动Visual Studio,加载WPFImages.sln。关键动作:右键项目 > 属性 > 应用程序 > 目标框架,确认为.NET Framework 4.7.2或更高(WPF的DPI感知在4.7.2才完善)。若目标框架低于此版本,VisualTreeHelper.GetDpi可能返回错误值。
注意:不要在
App.xaml中手动设置dpiAware="True"。WPF 4.7.2+默认启用Per-Monitor DPI Awareness,手动设置反而可能降级为System-Aware模式,导致多显示器场景下DPI计算错误。
4.2 调试配置:利用预置的Settings.settings快速验证
打开Properties\Settings.settings,将DefaultExportPath设为.\DebugExports(相对路径,便于清理)。DefaultWidth设为800,EnableScaling设为false。保存后,重新生成项目。此时Settings.Designer.cs会自动生成强类型属性,Settings.Default.DefaultExportPath即可直接调用。
4.3 在MainWindow中集成导出示例
打开MainWindow.xaml,添加一个ColorPicker(假设已引用Extended.Wpf.Toolkit或自研版本)和一个导出按钮:
<StackPanel Margin="20">
<xctk:ColorPicker x:Name="MyColorPicker" Width="300" Height="50"/>
<Button Content="导出为PNG" Click="ExportColorPicker_Click" Margin="0,10,0,0"/>
</StackPanel>
在MainWindow.xaml.cs中添加事件处理:
private void ExportColorPicker_Click(object sender, RoutedEventArgs e)
{
string fileName = $"ColorPicker_{DateTime.Now:yyyyMMdd_HHmmss}.png";
string fullPath = Path.Combine(Settings.Default.DefaultExportPath, fileName);
bool success = ExportService.ExportToPng(MyColorPicker, fullPath);
if (success)
{
MessageBox.Show(string.Format(Resources.ExportSuccessMessage, fullPath));
}
else
{
MessageBox.Show("导出失败,请检查控件状态和路径权限。");
}
}
4.4 执行导出与结果验证
按F5运行程序,点击“导出为PNG”。观察DebugExports文件夹是否生成PNG文件。用Photoshop打开,检查以下三点:
- 尺寸验证:图像像素宽高应为800 × 50(DIP尺寸),但因150% DPI,实际物理像素为1200 × 75。在Photoshop的“图像大小”对话框中,分辨率应显示为144 ppi(96 × 1.5)。
- 透明度验证:用魔棒工具点击背景,应选中全部透明区域(而非黑色)。图层混合模式设为“正片叠底”,背景应完全消失。
- 细节验证:放大至400%,检查ColorPicker的滑块轨道、色块分界线,应无模糊或锯齿。特别是圆角部分,应与屏幕上显示完全一致。
实操心得:首次导出失败?90%概率是
MyColorPicker的Visibility为Collapsed或IsLoaded为false。在ExportColorPicker_Click开头加一行Debugger.Break(),用即时窗口检查MyColorPicker.IsLoaded和MyColorPicker.Visibility。我曾因此浪费2小时,后来把它写进了ExportService的调试日志里。
5. 常见问题与排查技巧实录:那些文档里不会写的“血泪经验”
以下是我在真实项目中遇到的TOP5问题,附带可直接复制的排查命令和修复代码。
5.1 问题速查表
| 问题现象 | 可能原因 | 快速排查命令 | 修复方案 |
|---|---|---|---|
| 导出图全黑 | 控件Background为null且未设置RenderTargetBitmap清屏色 | Debug.WriteLine($"BG: {element.Background}"); | 渲染前执行renderTarget.Clear(Colors.Transparent); |
| 图片尺寸比预期小50% | 系统DPI为200%,但未启用DPI适配逻辑 | Debug.WriteLine(VisualTreeHelper.GetDpi(element)); | 确保ExportService中调用GetDpi并按比例计算物理像素 |
| 圆角边缘发虚 | UseLayoutRounding未启用或BitmapScalingMode为LowQuality | Debug.WriteLine(element.UseLayoutRounding); | 在控件构造函数设UseLayoutRounding=true,渲染前设BitmapScalingMode.HighQuality |
| 文字出现灰色噪点 | TextOptions.TextRenderingMode为Auto(在离屏渲染中效果差) | Debug.WriteLine(TextOptions.GetTextRenderingMode(element)); | 渲染前设TextOptions.SetTextRenderingMode(element, TextRenderingMode.ClearType) |
| 导出后文件损坏(打不开) | FileStream未正确关闭或PngBitmapEncoder未设置Frames | 检查try-catch中是否有fileStream.Dispose() | 使用using包裹FileStream和PngBitmapEncoder,确保异常时释放 |
5.2 经典案例:解决“ColorPicker在高DPI下色块边界半透明灰边”
现象:在200% DPI下,ColorPicker的HSV色轮导出后,每个色块交界处有一像素宽的灰色过渡带,像抗锯齿过度。
根因分析:ColorPicker内部使用LinearGradientBrush绘制色轮,而LinearGradientBrush在离屏渲染时,若起点/终点坐标非整数,会触发亚像素插值。RenderTargetBitmap的默认采样算法(Bicubic)对此敏感。
排查过程:
1. 用Snoop工具检查ColorPicker的RenderTransform,确认无额外缩放。
2. 在ExportService中临时注释掉UseLayoutRounding设置,问题依旧,排除布局舍入问题。
3. 将RenderTargetBitmap的PixelWidth/PixelHeight改为Math.Floor取整(而非Ceiling),灰边消失但色轮轻微变形——证实是亚像素问题。
终极修复:
// 在渲染前,强制将色轮容器的RenderTransform设为整数像素偏移
if (element is FrameworkElement fe && fe.Tag?.ToString() == "ColorWheel")
{
// 获取色轮的实际渲染区域
Rect bounds = VisualTreeHelper.GetDescendantBounds(fe);
// 计算需补偿的亚像素偏移
double offsetX = Math.Floor(bounds.X) - bounds.X;
double offsetY = Math.Floor(bounds.Y) - bounds.Y;
fe.RenderTransform = new TranslateTransform(offsetX, offsetY);
}
同时,在ColorPicker的Loaded事件中,为色轮容器添加Tag="ColorWheel"标记。这样,导出时自动补偿亚像素,既保持精度又消除灰边。
5.3 进阶技巧:批量导出多个控件为单页PDF(非PNG)
虽然项目核心是PNG,但常有需求将一组控件(如RoundedCornerGenerator、ColorPicker、Slider)导出为一页PDF用于设计评审。这无需第三方库,纯WPF实现:
public static void ExportControlsToPdf(List<UIElement> elements, string pdfPath)
{
// 创建虚拟页面(A4尺寸,96dpi下为816×1133 DIP)
FixedDocument doc = new FixedDocument();
PageContent pageContent = new PageContent();
FixedPage page = new FixedPage();
double y = 50; // 起始Y坐标
foreach (var elem in elements)
{
// 为每个控件生成PNG流
MemoryStream pngStream = new MemoryStream();
ExportToPng(elem, pngStream); // 重载版,输出到Stream
// 将PNG转为BitmapImage并添加到FixedPage
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = pngStream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
Image imageControl = new Image { Source = bitmap, Width = 300 };
Canvas.SetLeft(imageControl, 50);
Canvas.SetTop(imageControl, y);
FixedPage.SetZIndex(imageControl, 0);
page.Children.Add(imageControl);
y += 350; // 下一个控件的Y位置
}
((IAddChild)pageContent).AddChild(page);
doc.Pages.Add(pageContent);
// 保存为XPS(WPF原生支持),再用免费工具转PDF
XpsDocument xpsDoc = new XpsDocument(pdfPath.Replace(".pdf", ".xps"), FileAccess.Write);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
writer.Write(doc);
xpsDoc.Close();
}
提示:WPF原生不支持PDF输出,但XPS是微软标准格式,可用
Microsoft Print to PDF虚拟打印机将XPS转PDF,或用开源库iTextSharp(需.NET Framework)进行转换。此方案避免了引入大型PDF库,保持项目轻量。
6. 扩展与定制:如何将此工具嵌入你的现有WPF项目
这个工具的设计哲学是“零侵入”。你不需要修改现有项目结构,只需3步即可集成。
6.1 方式一:直接引用WPFImages.dll(推荐给企业级项目)
- 在你的主项目中,右键“引用” > “添加引用” > “浏览”,选择
WPFImages\bin\Debug\WPFImages.dll。 - 在需要导出的页面代码中,添加
using WPFImages.Services;。 - 调用
ExportService.ExportToPng(myControl, @"C:\path\to\export.png");。
优势:完全隔离,WPFImages.dll的更新不影响主项目;可为不同项目配置不同的Settings.settings。
6.2 方式二:复制核心代码(推荐给小型工具或学习项目)
只需复制以下4个文件到你的项目:
- ExportService.cs(核心导出逻辑)
- Properties\Settings.settings(配置定义)
- Properties\Resources.resx(资源定义)
- Properties\AssemblyInfo.cs中添加[assembly: NeutralResourcesLanguage("en-US")]
然后在项目属性 > 应用程序 > 默认命名空间,改为你的项目名(如MyApp),Settings.Designer.cs会自动重生成。所有WPFImages.Properties命名空间需替换为MyApp.Properties。
注意:
Settings.settings的Access Modifier必须设为Public,否则其他项目无法访问Settings.Default。
6.3 方式三:作为NuGet包发布(团队协作场景)
若团队多人使用,可将其打包为私有NuGet包:
1. 在WPFImages.csproj中添加:
<PropertyGroup>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>WPFImages.Screenshot</PackageId>
<Version>1.0.0</Version>
<Authors>YourName</Authors>
<Description>WPF UIElement PNG Exporter with DPI support</Description>
</PropertyGroup>
- 右键项目 > “打包”,生成
.nupkg文件。 - 将
.nupkg放到公司内部NuGet源(如Azure Artifacts或本地文件夹),团队成员即可在NuGet包管理器中搜索安装。
我个人在三个不同客户项目中都采用了方式一,因为它让截图逻辑与业务逻辑彻底解耦。有一次客户要求“导出时自动添加水印”,我只在ExportService.cs里加了12行代码(用DrawingVisual绘制半透明文字覆盖在渲染图上),主项目一行代码都不用改——这才是可维护性的体现。
7. 最后的实操体会:关于“像素级保真”的再思考
做完这个工具后,我和团队做了个对比实验:用Snipaste截取RoundedCornerGenerator,用Paint.NET打开同一张图,再用本工具导出同一控件。放大到800%,Snipaste截图的圆角边缘有2像素宽的抗锯齿过渡,Paint.NET打开后颜色轻微偏移(sRGB vs Adobe RGB色彩空间问题),而本工具导出的PNG,像素值与WPF渲染引擎输出的帧缓冲区完全一致——每一个RGBA值都精确匹配。
这让我意识到,“截图工具”的终极价值,从来不是“够用就行”,而是成为开发者的可信度锚点。当UI设计师说“这个圆角在Figma里是8px”,你可以立刻导出PNG,用取色器验证;当测试报告指出“ColorPicker在200% DPI下色块错位”,你可以秒级复现并定位到LinearGradientBrush的坐标计算偏差。它把模糊的“看起来像”,变成了确定的“数值等于”。
所以,如果你正在为某个自定义控件写文档,别再截图了——用ExportToPng;如果你在写自动化测试,别再依赖外部截图工具了——把ExportService注入你的测试基类;甚至,下次和设计师争论“这个阴影的模糊半径到底是12还是14”,直接导出两张PNG,用Beyond Compare的像素比对模式说话。工具的价值,不在于它多炫酷,而在于它能否让你少说一句“我觉得”,多说一句“你看,这里是证据”。
简介:直接对WPF界面中的任意UIElement(如Panel、UserControl、RoundedCornerGenerator、ColorPicker等自定义控件)进行像素级截图并保存为PNG文件。底层基于RenderTargetBitmap实现,完整保留Alpha通道,支持透明背景输出;可自由设置导出尺寸、缩放比例,并自动适配系统DPI缩放设置。项目已集成配置管理,通过Settings.settings持久化存储常用参数(如默认导出路径、宽高偏好、是否启用缩放等),Resources.resx预留多语言界面扩展能力。工程结构清晰,已排除bin/obj目录,开箱即用:加载WPFImages.sln后,调用ExportToPng方法传入目标元素即可完成导出。适用于UI设计稿归档、自动化测试截图比对、原型评审素材准备、文档插图生成等实际开发场景。


被折叠的 条评论
为什么被折叠?



