typescript
复制代码
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { image } from '@kit.ImageKit';
import { fileIo } from '@kit.CoreFileKit';
import { display, Prompt } from '@kit.ArkUI';
@Component
export struct CameraView {
private context: Context | undefined = this.getUIContext().getHostContext();
cameraHelper: CameraHelper = new CameraHelper(this.context!);
aboutToAppear() {
this.cameraHelper.setTakePictureCallback((path) => {
Prompt.showToast({ message: path })
})
}
@Builder
uiBuilder() {
XComponent({
id: 'cameraPreview',
type: XComponentType.SURFACE,
controller: this.cameraHelper.getController()
})
.onLoad(() => {
this.cameraHelper.requestPermissionsFn(() => {
this.cameraHelper.initCamera().then();
});
})
.width("100%")
.height("100%")
.backgroundColor(Color.Black)
}
build() {
this.uiBuilder()
}
}
class CameraHelper {
private context: Context | undefined;
constructor(context: Context) {
this.context = context;
}
getScreenWidth(): number {
return display.getDefaultDisplaySync().width;
}
getScreenHeight(): number {
return display.getDefaultDisplaySync().height;
}
private sWidth: number = this.getScreenWidth()
private sHeight: number = this.getScreenHeight()
private session: camera.PhotoSession | undefined = undefined;
private cameras: Array<camera.CameraDevice> = [];
private cameraInput: camera.CameraInput | undefined = undefined;
private previewOutput: camera.PreviewOutput | undefined = undefined;
private photoOutput: camera.PhotoOutput | undefined = undefined;
private selectCameraPosition: number = camera.CameraPosition.CAMERA_POSITION_FRONT;
private onTakePictureCallback: ((imagePath: string) => void) | undefined = undefined;
private cameraManager: camera.CameraManager | undefined = undefined;
private xComponentSurfaceId: string = '';
private xComponentCtl: XComponentController = new XComponentController();
// 获取控制器
getController(): XComponentController {
return this.xComponentCtl;
}
// 设置拍照回调
setTakePictureCallback(callback: (imagePath: string) => void): void {
this.onTakePictureCallback = callback;
}
// 请求相机权限
async requestPermissionsFn(success?: (result: boolean) => void): Promise<void> {
let atManager = abilityAccessCtrl.createAtManager();
if (this.context) {
let res = await atManager.requestPermissionsFromUser(this.context, ['ohos.permission.CAMERA']);
for (let i = 0; i < res.permissions.length; i++) {
if (res.permissions[i] === 'ohos.permission.CAMERA' && res.authResults[i] === 0) {
success?.(true);
}
}
}
}
// 拍照方法
async takePicture(): Promise<void> {
if (!this.photoOutput) {
Prompt.showToast({ message: 'PhotoOutput not initialized' })
return;
}
try {
let photoCaptureSetting: camera.PhotoCaptureSetting = {
quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
rotation: camera.ImageRotation.ROTATION_0,
mirror: this.isCameraFront()
};
console.info('开始拍照...');
await this.photoOutput.capture(photoCaptureSetting);
console.info('拍照成功完成');
} catch (error) {
let err = error as BusinessError;
console.error(`拍照异常, error code: ${err.code}`);
}
}
// 释放相机资源
async releaseCamera(): Promise<void> {
try {
if (this.session) {
await this.session.stop();
this.session.release();
this.session = undefined;
}
if (this.cameraInput) {
await this.cameraInput.close();
this.cameraInput = undefined;
}
this.photoOutput = undefined;
this.previewOutput = undefined;
} catch (error) {
let err = error as BusinessError;
console.error(`Release camera failed, error code: ${err.code}`);
}
}
// 判断是否为前置摄像头(保持不变)
isCameraFront(): boolean {
return this.selectCameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT;
}
/**
* 将ArrayBuffer保存为文件
*/
private async saveArrayBufferToFile(buffer: ArrayBuffer, fileName: string): Promise<string> {
const filePath = `${this.context!.filesDir}/${fileName}`;
let file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
// 写入数据
fileIo.writeSync(file.fd, buffer);
fileIo.closeSync(file.fd);
return filePath;
}
// 检查是否有多个摄像头可用(保持不变)
hasMultipleCameras(): boolean {
return this.cameras && this.cameras.length > 1;
}
// 切换摄像头(保持不变)
async switchCamera(): Promise<void> {
if (!this.hasMultipleCameras()) {
console.info('只有一个摄像头,无法切换');
return;
}
try {
if (this.isCameraFront()) {
this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_BACK;
} else {
this.selectCameraPosition = camera.CameraPosition.CAMERA_POSITION_FRONT;
}
await this.initCamera();
} catch (error) {
let err = error as BusinessError;
console.error(`切换摄像头失败, error code: ${err.code}`);
}
}
// 初始化相机
async initCamera(): Promise<void> {
await this.releaseCamera();
this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId();
try {
this.cameraManager = camera.getCameraManager(this.context);
if (!this.cameraManager) {
console.error('getCameraManager failed');
return;
}
this.cameras = this.cameraManager.getSupportedCameras();
if (!this.cameras || this.cameras.length === 0) {
console.error('No cameras found');
return;
}
const cameraDevice = this.cameras.find((v) => {
return v.cameraPosition === this.selectCameraPosition;
})
this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);
if (!this.cameraInput) {
console.error('createCameraInput failed');
return;
}
await this.cameraInput.open();
// 获取拍照模式的输出能力
let capability: camera.CameraOutputCapability =
this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_PHOTO);
if (!capability) {
// 如果拍照模式不支持,尝试使用视频模式获取预览配置
capability = this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_VIDEO);
}
if (!capability || capability.previewProfiles.length === 0) {
console.error('No preview profiles supported');
this.releaseCamera();
return;
}
// 选择预览配置
let previewProfile = this.selectBestPreviewProfile(capability.previewProfiles, this.sHeight, this.sWidth);
this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId);
if (!this.previewOutput) {
console.error('createPreviewOutput failed');
this.releaseCamera();
return;
}
// 创建拍照输出流
if (capability.photoProfiles && capability.photoProfiles.length > 0) {
let photoProfile = this.selectBestPreviewProfile(capability.photoProfiles, this.sHeight, this.sWidth);
this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);
}
if (!this.photoOutput) {
console.error('createPhotoOutput failed');
this.releaseCamera();
return;
}
// 创建拍照会话
this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
if (!this.session) {
console.error('createSession failed');
this.releaseCamera();
return;
}
// 配置会话
this.session.beginConfig();
this.session.addInput(this.cameraInput);
this.session.addOutput(this.previewOutput);
this.session.addOutput(this.photoOutput);
await this.session.commitConfig();
// 启动预览
await this.session.start();
console.info('摄像头预览启动成功');
// 设置拍照监听器
this.setupPhotoOutputListeners();
} catch (error) {
let err = error as BusinessError;
console.error(`Camera initialization failed, error code: ${err.code}`);
}
}
// 选择最佳预览配置的方法
private selectBestPreviewProfile(
previewProfiles: camera.Profile[],
targetWidth: number,
targetHeight: number
): camera.Profile {
if (previewProfiles.length === 0) {
throw new Error('No preview profiles available');
}
// 计算目标宽高比
const targetAspectRatio = targetWidth / targetHeight;
let bestProfile = previewProfiles[0];
let minAspectDiff = Number.MAX_VALUE;
let minSizeDiff = Number.MAX_VALUE;
for (const profile of previewProfiles) {
const profileAspectRatio = profile.size.width / profile.size.height;
const aspectDiff = Math.abs(profileAspectRatio - targetAspectRatio);
// 优先选择宽高比最接近的配置
if (aspectDiff < minAspectDiff) {
minAspectDiff = aspectDiff;
bestProfile = profile;
minSizeDiff = Math.abs(profile.size.width - targetWidth) +
Math.abs(profile.size.height - targetHeight);
}
// 如果宽高比相同,选择尺寸最接近的
else if (Math.abs(aspectDiff - minAspectDiff) < 0.01) {
const sizeDiff = Math.abs(profile.size.width - targetWidth) +
Math.abs(profile.size.height - targetHeight);
if (sizeDiff < minSizeDiff) {
minSizeDiff = sizeDiff;
bestProfile = profile;
}
}
}
console.info(`最佳匹配: ${bestProfile.size.width}x${bestProfile.size.height}, 宽高比: ${bestProfile.size.width /
bestProfile.size.height}`);
return bestProfile;
}
// 设置拍照输出流监听器
private setupPhotoOutputListeners(): void {
if (!this.photoOutput) {
return;
}
this.photoOutput.on('photoAvailable', (err: BusinessError, photo: camera.Photo) => {
if (err) {
console.error(`获取照片失败: ${err.code}`);
return;
}
// 处理照片数据
this.processPhoto(photo);
});
}
// 处理照片
private async processPhoto(photo: camera.Photo): Promise<void> {
photo.main.getComponent(image.ComponentType.JPEG, async (err: BusinessError, component: image.Component) => {
if (err || !component) {
Prompt.showToast({ message: `获取JPEG组件失败: ${err?.code}` })
return;
}
try {
const fileName = `checkSkin${Date.now()}.jpg`;
const buffer = component.byteBuffer;
const filePath = await this.saveArrayBufferToFile(buffer, fileName);
this.onTakePictureCallback?.(filePath)
// 释放资源
photo.main.release();
} catch (error) {
photo.main.release();
}
});
}
}