HarmonyOS鸿蒙Next中pc如何实现截图功能
HarmonyOS鸿蒙Next中pc如何实现截图功能 鸿蒙pc端如何实现截图功能,流程是什么样的
【背景知识】
HarmonyOS开发中有三种截图方式,分别是组件截图、窗口截图和屏幕截图:
- getComponentSnapshot组件截图:提供获取组件截图的能力,包括已加载的组件的截图和没有加载的组件的截图。组件截图只能够截取组件大小的区域,如果组件的绘制超出了它的区域,或子组件的绘制超出了父组件的区域,这些在组件区域外绘制的内容不会在截图中呈现。兄弟节点堆叠在组件区域内,截图不会显示兄弟组件。
- snapshot:提供窗口截图能力,使用前需要用getLastWindow()、createWindow()、findWindow()中的任一方法获取到Window实例(windowClass)。
- 屏幕截图:提供屏幕截图能力,根据screenshot.pick和screenshot.capture文档描述,仅2in1设备或平板设备可以使用,如果是手机使用则会报错:“Failed to pick. Code: {“code”:801}”。
其中常见的截图方式为组件截图、窗口截图,两者区别如下:
| 维度 | 组件截图 | 窗口截图 |
|---|---|---|
| 截取范围 | 组件区域内。 | 整个window窗口。 |
| 技术实现 | componentSnapshot.get。 | window.getLastWindow获取窗口后win.snapshot。 |
| 安全性 | 更安全(可避免敏感信息泄露)。 | 需处理敏感信息(如密码、支付界面)。 |
| 业务场景 | 组件内容转成图片,比如商品信息。 | 用户分享界面、崩溃时自动截取上下文发送报告。 |
【解决方案】
import { window } from '@kit.ArkUI';
import { image } from '@kit.ImageKit';
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct Snapshot {
@State pixmap: PixelMap | string = '';
build() {
Column() {
Button('点击').onClick(() => {
let context = this.getUIContext().getHostContext() as Context as common.UIAbilityContext;
window.getLastWindow(context).then(win => {
let promise = win.snapshot();
promise.then((pixelMap: image.PixelMap) => {
this.pixmap = pixelMap;
});
});
});
if (this.pixmap) {
Image(this.pixmap)
.borderWidth(1)
.width(300);
}
}
.width('100%')
.height('100%')
.backgroundColor(Color.Pink);
}
}
- 屏幕截图实现: 参考官网screenshot.capture实现。
- 截图常见场景。
- 截图后保存到相册。 三种截图方式在调用API后都会返回image.PixelMap数据,这时可以利用SaveButton实现图片保存到相册,完整示例代码如下:
1.在EntryAbility.ets中保存windowStage。
import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
const DOMAIN = 0x0000;
export default class EntryAbility extends UIAbility {
onCreate(): void {
try {
this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
} catch (err) {
hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err));
}
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
AppStorage.setOrCreate('windowStage', windowStage);
// Main window is created, set main page for this ability
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
AppStorage.setOrCreate('windowStage',windowStage);
hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
}
};
2.WatermarkShot.ets。
import { image } from '@kit.ImageKit';
import { window } from '@kit.ArkUI';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
export interface ImagePixelMap {
pixelMap: image.PixelMap;
width: number;
height: number;
}
export async function imageSource2PixelMap(pixelMap: image.PixelMap): Promise<ImagePixelMap> {
return pixelMap.getImageInfo().then((imageInfo: image.ImageInfo) => {
const height = imageInfo.size.height;
const width = imageInfo.size.width;
const result: ImagePixelMap = { pixelMap, width, height };
return result;
});
}
export function addWatermark(
imagePixelMap: ImagePixelMap,
drawWatermark?: (OffscreenContext: OffscreenCanvasRenderingContext2D) => void
): image.PixelMap {
let windowStage = AppStorage.get('windowStage') as window.WindowStage;
const height = windowStage.getMainWindowSync().getUIContext().px2vp(imagePixelMap.height);
const width = windowStage.getMainWindowSync().getUIContext().px2vp(imagePixelMap.width);
const offScreenCanvas = new OffscreenCanvas(width, height);
const offScreenContext = offScreenCanvas.getContext('2d');
offScreenContext.drawImage(imagePixelMap.pixelMap, 0, 0, width, height);
if (drawWatermark) {
drawWatermark(offScreenContext);
} else {
offScreenContext.fillStyle = '#10000000';
offScreenContext.font = '16vp';
const rotationAngle = -30;
const watermarkHeight = 120;
const watermarkWidth = 120;
const watermarkText = '水印水印水印';
const colCount = Math.ceil(imagePixelMap.height / watermarkHeight);
const rowCount = Math.ceil(imagePixelMap.width / watermarkWidth);
for (let col = 0; col <= colCount; col++) {
let row = 0;
for (; row <= rowCount; row++) {
const angle = rotationAngle * Math.PI / 180;
offScreenContext.rotate(angle);
const positionX = rotationAngle > 0 ? watermarkHeight * Math.tan(angle) : 0;
const positionY = rotationAngle > 0 ? 0 : watermarkWidth * Math.tan(-angle);
offScreenContext.fillText(watermarkText, positionX, positionY);
offScreenContext.rotate(-angle);
offScreenContext.translate(0, watermarkHeight);
}
offScreenContext.translate(0, -watermarkHeight * row);
offScreenContext.translate(watermarkWidth, 0);
}
}
return offScreenContext.getPixelMap(0, 0, width, height);
}
@Entry
@Component
struct WatermarkShot {
@State pixmap: image.PixelMap | undefined = undefined;
saveButtonOptions: SaveButtonOptions = {
icon: SaveIconStyle.FULL_FILLED,
text: SaveDescription.SAVE_IMAGE,
buttonType: ButtonType.Capsule
};
private saveToPhoto() {
let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
const imagePackerApi = image.createImagePacker();
imagePackerApi.packToData(this.pixmap, packOpts).then(async (buffer: ArrayBuffer) => {
let file: fs.File | undefined = undefined;
try {
let context = this.getUIContext().getHostContext() as Context as common.UIAbilityContext;
let helper = photoAccessHelper.getPhotoAccessHelper(context);
let uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg');
file = fs.openSync(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.writeSync(file.fd, buffer);
fs.closeSync(file);
} catch (error) {
try {
if (file) {
fs.close(file);
}
} catch (e) {
}
}
});
}
build() {
Column() {
Row() {
Text('添加水印页面');
}.margin({ top: 30, bottom: 200 });
SaveButton(this.saveButtonOptions)
.onClick(() => {
let context = this.getUIContext().getHostContext() as Context as common.UIAbilityContext;
window.getLastWindow(context).then(win => {
let promise = win.snapshot();
promise.then(async (pixelMap: image.PixelMap) => {
const imagePixelMap = await imageSource2PixelMap(pixelMap);
this.pixmap = addWatermark(imagePixelMap);
this.saveToPhoto();
}).catch((err: BusinessError) => {
console.error(`Failed to snapshot window. Cause code: ${err.code}, message: ${err.message}`);
});
});
});
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center);
}
}
更多关于HarmonyOS鸿蒙Next中pc如何实现截图功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
[@ohos.screenshot](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-screenshot#screenshotpick) 提供屏幕截图的能力。
capture(options?: CaptureOption): Promise<image.PixelMap>
获取屏幕全屏截图,使用Promise异步回调。
此接口可以通过设置不同的displayId截取不同屏幕的截图,且只能截取全屏;pick接口可实现区域截屏。
需要权限:
API version 22前,需申请ohos.permission.CAPTURE_SCREEN权限;
从API version 22开始,需要申请ohos.permission.CAPTURE_SCREEN权限或ohos.permission.CUSTOM_SCREEN_RECORDING权限。
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
let captureOption: screenshot.CaptureOption = {
"displayId": 0
};
try {
let promise = screenshot.capture(captureOption);
promise.then((pixelMap: image.PixelMap) => {
console.info('Succeeded in saving screenshot. Pixel bytes number: ' + pixelMap.getPixelBytesNumber());
pixelMap.release(); // PixelMap使用完后及时释放内存
}).catch((err: BusinessError) => {
console.error(`Failed to save screenshot. Code: ${err.code}, message: ${err.message}`);
});
} catch (exception) {
console.error(`Failed to save screenshot. Code: ${exception.code}, message: ${exception.message}`);
};
可以直接调用系统提供的组件级截图接口 getUIContext().getComponentSnapshot(),把任意组件输出成 PixelMap,再借助 image.ImagePacker 生成 PNG 文件,最后通过 photoAccessHelper 存入系统相册。该方案还能通过循环滚屏和拼接实现“长截图”效果,适合需要对应用内部界面做精细化截图的场景
在HarmonyOS鸿蒙Next中,PC端可通过系统API实现截图功能。主要使用ScreenCapture类,调用captureScreen()方法获取屏幕图像数据。开发者需申请ohos.permission.CAPTURE_SCREEN权限,并在配置文件中声明。截取图像后,可保存为PixelMap对象或指定格式文件。
在HarmonyOS Next的PC应用开发中,实现截图功能的核心是使用ScreenCapture(屏幕捕获)相关API。主要流程如下:
-
申请权限:首先需要在应用的
module.json5配置文件中声明必要的权限,例如ohos.permission.CAPTURE_SCREEN(捕获屏幕)用于全屏截图。如果涉及隐私区域,需同步进行动态权限申请。 -
创建ImageReceiver:通过
image.createImageReceiver()方法创建一个ImageReceiver对象,并设置其参数(如尺寸、格式)。它将作为截图图像的接收器。 -
获取窗口/屏幕对象:若要截取当前应用窗口,可通过
window.getLastWindow()获取窗口对象。若要截取整个屏幕,则需要使用ScreenCapture相关接口获取屏幕对象。 -
执行截图:
- 窗口截图:调用窗口对象的
capture()方法,将前面创建的ImageReceiver传入,即可捕获该窗口的图像。 - 屏幕截图:使用
ScreenCapture模块的接口(如captureScreen)并指定ImageReceiver来捕获整个屏幕。
- 窗口截图:调用窗口对象的
-
处理图像数据:截图操作是异步的。需要监听
ImageReceiver的on('imageArrival')事件。当事件触发时,从ImageReceiver中获取最新的Image对象,然后可以将其转换为PixelMap进行进一步处理(如保存到文件、显示在UI上或进行编辑)。
关键代码结构示意(以窗口截图为例):
// 1. 创建ImageReceiver
let imageReceiver = image.createImageReceiver(720, 1280, 4, 8); // 示例参数
// 2. 监听图像到达事件
imageReceiver.on('imageArrival', () => {
let img = imageReceiver.readNextImage();
if (img) {
// 获取PixelMap并处理,例如保存
let pixelMap = img.createPixelMap();
// ... 保存pixelMap到文件或显示
img.release(); // 释放资源
}
});
// 3. 获取窗口并截图
let windowClass = null;
try {
windowClass = window.getLastWindow(this.context);
} catch (err) {
// 错误处理
}
if (windowClass) {
windowClass.capture(imageReceiver).then(() => {
// 截图已触发,等待imageArrival事件
});
}
注意事项:
- 隐私与权限:截取非应用自身的窗口或全屏涉及用户隐私,必须明确告知用户并获得授权(动态权限弹窗),且通常需要在系统设置中开启相关权限开关。
- 性能:截图操作,尤其是全屏截图,涉及大量图像数据传输,应注意频率,避免性能开销。
- API差异:HarmonyOS Next的API与旧版本HarmonyOS可能存在差异,开发时请务必以当前版本的官方API文档和DevEco Studio中的接口提示为准。
建议直接查阅HarmonyOS Next的官方开发文档(特别是窗口管理和屏幕捕获相关章节)和API参考,以获取最准确、详细的接口定义、参数说明和示例代码。

