/* * 相机控制 * */ import router from '@ohos.router'; import image from '@ohos.multimedia.image'; import fs from "@ohos.file.fs" import CommonEventManager from '@ohos.commonEventManager' import promptAction from '@ohos.promptAction'; import { ConfirmDialogParams } from '../viewmodel/ConfirmDialogParam'; import { ConfirmDialog } from '../view/ConfirmDialog'; import { PreviewManager } from '../common/util/PreviewManager'; const TAG = "sony_camera info" @Entry @Component struct CameraControl { @StorageLink('CameraStatus') cameraStatus: boolean = false; commonDialogController: CustomDialogController | null = null; build() { Row() { Column() { Row() { }.width('100%') .height('3.4%') Row(){ Row() { Image($r('app.media.general_return')) .height($r('app.float.virtualSize_56')) .width($r('app.float.virtualSize_56')) .fillColor($r('app.color.FFFFFF')) Text('图像采集') .fontColor($r('app.color.FFFFFF')) .fontSize($r('app.float.fontSize_30')) } .width('75%') .justifyContent(FlexAlign.Start) .margin({ left: '3%' }) .onClick(() => { router.back() }) } .height('4%').width('100%').justifyContent(FlexAlign.Start) Row(){ Column(){ Row(){ Image($r('app.media.device_multimedia_acquisition')) .width('70%') .objectFit(ImageFit.Contain) .interpolation(ImageInterpolation.High) } .height('40%') .width('100%') //.margin({top:'5%'}) .alignItems(VerticalAlign.Bottom) .justifyContent(FlexAlign.Center) Column({space:10}){ InfoRow({ label:"编号:", value:"DF441AS114F5555", }) InfoRow({ label:"状态:", value:this.cameraStatus?"在线":"离线", valueColor:$r('app.color.30D158') }) InfoRow({ label:"计量有效期:", value:"2024/11/11~2025/11/11", }) InfoRow({ label:"像素:", value:"2420万像素", }) InfoRow({ label:"数码变焦:", value:"8倍", }) InfoRow({ label:"对焦方式:", value:"自动对焦", }) InfoRow({ label:"闪光模式:", value:"自动,不闪光,强制闪光", }) }.justifyContent(FlexAlign.Start).margin({left:'10%'}) }.width('22%').justifyContent(FlexAlign.SpaceAround).height('100%') Divider() .vertical(true) .strokeWidth(1) .color($r('app.color.15FFFFFF')) .margin({top:'10%'}) Column(){ CameraDialog({}) .height('90%') }.justifyContent(FlexAlign.SpaceAround).margin({top:'5%'}) .height('100%') .width('70%') }.height('80%').width('100%').justifyContent(FlexAlign.SpaceAround) } .width('100%') .height('100%') .backgroundColor($r('app.color.000000')) } .height('100%') } } @Component struct InfoRow { label: string = '' @Prop value: string = '' valueColor?: Resource = $r('app.color.FFFFFF') // 默认白色,可覆盖 build() { Row({ space: 5 }) { Circle() .width($r('app.float.virtualSize_5')) .height($r('app.float.virtualSize_5')) .fill($r('app.color.FFFFFF')) Text(this.label) .fontColor($r('app.color.FFFFFF')) .fontSize($r('app.float.fontSize_16')) Text(this.value) .fontColor(this.valueColor) .fontSize($r('app.float.fontSize_16')) } .width('100%') } } @Component export struct CameraDialog { private scrollerPhoto: Scroller = new Scroller() // @State isExpanded: boolean = false //闪光模式 0:自动 1:开启 2:关闭 @State selectedFlashMode:number = 0 //选择的照片索引 @State selectedPhotoIndex:number = -1 //拍照按键缩放 @State shootButtonClick:number =1 // 旋转角度 @State rotateAngle: number = 0 // 照片缩放比例 @State scaleValue: number = 1 // 照片X轴偏移 @State offsetX: number = 0 // 照片Y轴偏移 @State offsetY: number = 0 // 照片上次缩放值 @State lastScale: number = 1 // 照片上次X偏移 @State lastOffsetX: number = 0 // 照片上次Y偏移 @State lastOffsetY: number = 0 //双指缩放的中心点X @State pinchCenterX: number = 0; //双指缩放的中心点Y @State pinchCenterY: number = 0; // 照片列表 //@State photoList:DrawingInfo[]=[] // 获取本地live照片 @State commodityPixelMap: PixelMap | null = null; // 拍照动作是否完成 @State isCapturing: boolean = false; // 是否停止预览 @State isStopView: boolean = false; // 控制帧率 @State readTimer:number = 0 // 预览操作 private previewManager: PreviewManager | null = null; @State photoPixelMaps: photoInfo[] = []; // 存储所有照片的 PixelMap // 加载照片并生成 PixelMap loadPhotos = async () => { const context = getContext(this); console.info(TAG, '开始加载照片', context.filesDir); try { const localImageDir = `${context.filesDir}/`; // 检查目录是否存在 if (!fs.accessSync(localImageDir)) { console.warn(TAG, "目录不存在"); return; } // 获取目录下所有文件 const files = fs.listFileSync(localImageDir); const newPixelMaps: photoInfo[] = []; // 加载所有以image_开头的图片 for (const file of files) { // 只处理以image_开头的文件 if (!file.startsWith('image_') || !file.endsWith('.jpg')) { continue; } const filePath = `${localImageDir}${file}`; try { const imageSource = image.createImageSource(filePath); const imageInfo = await imageSource.getImageInfo(); const scaleRatio = Math.min(600 / imageInfo.size.width, 800 / imageInfo.size.height); const thumbnailOpts: image.InitializationOptions = { size: { width: Math.floor(imageInfo.size.width * scaleRatio), height: Math.floor(imageInfo.size.height * scaleRatio) } } const pixelMap = await imageSource.createPixelMap(thumbnailOpts); const timestamp = this.parseTimestampFromFilename(file); newPixelMaps.push({ pixelMap: pixelMap, timestamp: timestamp, filename: file }); imageSource.release(); } catch (err) { console.error(TAG, `加载图片 ${file} 失败:`, err); } } // 按时间戳降序排序 newPixelMaps.sort((a, b) => b.timestamp.localeCompare(a.timestamp)); this.photoPixelMaps = newPixelMaps; } catch (err) { console.error(TAG, "加载照片失败:", err); } }; private parseTimestampFromFilename(filename: string): string { if (!filename.startsWith("image_") || !filename.endsWith(".jpg")) { return ""; } const dateTimePart = filename.substring(6, filename.length - 4); if (dateTimePart.length !== 15) { return ""; } const datePart = dateTimePart.substring(0, 8); const timePart = dateTimePart.substring(9); const year = datePart.substring(0, 4); const month = datePart.substring(4, 6); const day = datePart.substring(6, 8); const hour = timePart.substring(0, 2); const minute = timePart.substring(2, 4); const second = timePart.substring(4); return `${year}/${month}/${day} ${hour}:${minute}:${second}`; } private releaseAllPixelMaps() { this.photoPixelMaps.forEach(item => item.pixelMap?.release()); this.photoPixelMaps = []; } // 删除照片 deletePhoto = async (index: number) => { try { const context = getContext(this); const photoToDelete = this.photoPixelMaps[index]; const filePath = `${context.filesDir}/${photoToDelete.filename}`; console.info(TAG, `要删除文件: ${filePath}`); // 1. 删除物理文件 fs.unlinkSync(filePath); console.info(TAG, `已删除文件: ${filePath}`); // 2. 释放PixelMap资源 photoToDelete.pixelMap.release(); // 3. 从数组中移除 const newPhotoList = [...this.photoPixelMaps]; newPhotoList.splice(index, 1); this.photoPixelMaps = newPhotoList; // 4. 处理选中的照片索引 if (this.photoPixelMaps.length === 0) { // 如果所有照片都删完了 this.selectedPhotoIndex = -1; this.startView(); } else { // 还有照片剩余的情况 if (this.selectedPhotoIndex === index) { // 当前删除的是正在显示的照片 if (index > 0) { // 如果不是第一张,显示前一张 this.selectedPhotoIndex = index - 1; } else { // 如果是第一张,显示下一张(现在index=0,所以下一张也是0) this.selectedPhotoIndex = 0; } } else if (this.selectedPhotoIndex > index) { // 删除的照片在当前显示照片之前,需要调整索引 this.selectedPhotoIndex -= 1; } // 如果删除的照片在当前显示照片之后,不需要调整索引 } promptAction.showToast({ message: '照片删除成功', duration: 2000 }); } catch (err) { console.error(TAG, "删除照片失败:", err.code); promptAction.showToast({ message: '删除照片失败', duration: 2000 }); } }; //创建订阅者 subscriber: CommonEventManager.CommonEventSubscriber | null = null; //订阅相机回调 subscribeInfo: CommonEventManager.CommonEventSubscribeInfo = { events: ["sonycamera_callback"] }; //提示确认弹窗 commonDialogController: CustomDialogController | null = null; private showConfirmDialog(params: ConfirmDialogParams) { if (this.commonDialogController) { this.commonDialogController.close() } this.commonDialogController = new CustomDialogController({ builder: ConfirmDialog({ title: params.title || '提示', message: params.message, onConfirm: params.onConfirm }), cancel: () => console.log('用户取消操作'), customStyle: true, autoCancel:false, maskColor: 'rgba(0,0,0,0.6)' }); this.commonDialogController.open(); } //订阅回调(code=3代表连接成功,code=2代表拍照成功) createSubscriber = async () => { this.subscriber = await CommonEventManager.createSubscriber(this.subscribeInfo); if (this.subscriber) { console.info(TAG, "创建订阅回调成功"); CommonEventManager.subscribe(this.subscriber, (err, data) => { if (err?.code) { console.error(TAG, "SubscribeCallBack err=" + JSON.stringify(err)); return; } console.info(TAG, "SubscribeCallBack data=" + JSON.stringify(data)); if (data?.code !== undefined) { switch (data.code) { case 2: this.loadPhotos() this.isCapturing =false this.startView(); // 拍照成功 break; case 3: console.info(TAG, "开始预览"); this.startView(); // 连接成功 break; case -1: console.info(TAG,'连接故障') break; case 5: console.info(TAG,'暂停预览') break; } } //0001:自动闪光 0002:关闭闪光 0003:开启闪光 if (data?.parameters?.flash_mode) { const flashMode:string = data.parameters.flash_mode console.info(TAG, `收到闪光模式: ${flashMode}`); switch (flashMode) { case '0001': this.selectedFlashMode = 0 break; case '0002': this.selectedFlashMode = 2 break; case '0003': this.selectedFlashMode = 1 break; default: console.warn(TAG, `未知闪光模式: ${flashMode}`); } } }); } }; //旋转照片 private rotateImage(angle: number) { this.offsetX = 0; this.offsetY = 0; this.rotateAngle += angle if (this.rotateAngle >= 360) { this.rotateAngle -= 360 } else if (this.rotateAngle < 0) { this.rotateAngle += 360 } if(this.rotateAngle==90||this.rotateAngle==270) { this.scaleValue=0.667 }else { this.scaleValue=1 } } // 重置图片变换 private resetImageTransform() { this.rotateAngle = 0 this.scaleValue = 1 this.offsetX = 0 this.offsetY = 0 this.lastScale = 1 this.lastOffsetX = 0 this.lastOffsetY = 0 } //连接相机 connectCamera = async () => { CommonEventManager.publish("opensession", (err) => { if (err?.code) { console.info(TAG,"Publish openSession err=" + JSON.stringify(err)) } else { console.info(TAG,"Publish openSession succeed ") this.queryFlashMode(); } }) } //查询闪光模式 queryFlashMode = async () => { CommonEventManager.publish("queryflashmode", (err) => { if (err?.code) { console.info(TAG,"Publish queryflashmode err=" + JSON.stringify(err)) } else { console.info(TAG,"Publish queryflashmode succeed ") } }) } //开始预览 startView=async()=>{ CommonEventManager.publish("startview", (err) => { if (err?.code) { console.error(TAG, JSON.stringify(err)); } else { console.info(TAG, 'publish startview succeed'); this.isStopView =false; this.liveShow(); } }); } //停止预览 stopView=async()=>{ CommonEventManager.publish("stopview", (err) => { if (err?.code) { console.error(TAG, JSON.stringify(err)); } else { this.isStopView =true; console.info(TAG, 'publish stopview succeed'); if (this.readTimer) { clearInterval(this.readTimer); this.readTimer = 0 ; } } }); } //重连相机 reconnectCamera=async()=>{ this.showConfirmDialog({ title: '重连USB', message: `请重连USB后点击确定!`, onConfirm: async()=> { await new Promise((resolve, reject) => { CommonEventManager.publish("stopview", (err) => { if (err) return reject(err); CommonEventManager.publish("closesession", (err) => { err ? reject(err) : resolve(); }); }); }); await new Promise((resolve, reject) => { CommonEventManager.publish("reconnect", (err) => { if (err) return reject(err); resolve(); }); }); promptAction.showToast({ message: '相机正在重连中...', duration: 3000, bottom: 500 }); await sleep(3000); await this.connectCamera() //await this.liveShow() } }); } liveShow = async () => { let index = 0; let retryCount = 0; const MAX_RETRIES = 10; // 最大重试次数 const previewLoop = async () => { if (!this.isStopView && !this.isCapturing) { try { const success = await this.previewManager!.processFrame(index); if (success) { this.commodityPixelMap = await this.previewManager!.getActiveBuffer(); retryCount = 0; } else { retryCount++; console.warn(TAG, "processFrame失败"); } } catch (error) { retryCount++; console.warn(TAG, "预览更新失败:", error); } } if (retryCount === MAX_RETRIES) { clearInterval(this.readTimer) this.readTimer = 0; console.info(TAG,"GG") return; //this.reconnectCamera(); } if (this.readTimer !== 0) { // 检查是否应该继续循环 this.readTimer = setTimeout(previewLoop, 50); } }; this.readTimer = setTimeout(previewLoop, 50); }; //断开相机(停止预览->关闭连接) disconnectCamera = async () => { if(!this.isStopView) { await this.stopView() } CommonEventManager.publish("closesession", (err) => { if (err?.code) { console.info(TAG,"Publish closesession err=" + JSON.stringify(err)) } else { console.info(TAG,"Publish closesession succeed ") } }) } //拍照(停止预览->拍照->开始预览) takePhoto = async () => { this.isStopView = true; this.isCapturing = true; CommonEventManager.publish("stopview", (err) => { if (err?.code) { console.info(TAG,"Publish stopview err=" + JSON.stringify(err)); } else { console.info(TAG,"Publish Publish stopview succeed"); CommonEventManager.publish("shootandsave", (err) => { if (err?.code) { console.info(TAG,"Publish shootandsave error=" + JSON.stringify(err)); } else { console.info(TAG,"Publish Publish shootandsave succeed"); } }); } }); }; //设置闪光模式 setFlashMode = (mode: 'openflash' | 'closeflash' | 'autoflash') => { this.isStopView = true; CommonEventManager.publish("stopview", (err) => { if (err?.code) { console.info(TAG,"Publish stopview err=" + JSON.stringify(err)); } else { console.info(TAG,"Publish Publish stopview succeed"); CommonEventManager.publish(mode, (err) => { if (err?.code) { console.info(TAG,"Publish flashmode error=" + JSON.stringify(err)); } else { console.info(TAG,"Publish Publish flashmode succeed"); CommonEventManager.publish("startview", (err) => { this.isStopView = false; if (err?.code) { console.info(TAG,"Publish startview error=" + JSON.stringify(err)); }else{ console.info(TAG,"Publish Publish startview succeed"); } }) } }); } }); } private adjustOffsetWithAnimation() { const isRotated = (this.rotateAngle === 90 || this.rotateAngle === 270); //旋转90度或者70度 宽度0.667倍,长度超过1.5倍才拖动 let moveScaleY = (this.scaleValue - (isRotated ? 0.667 : 1)) / 2; let moveScaleX = (this.scaleValue - (isRotated ? 1.50 : 1)) / 2; let maxOffsetX = (this.scaleValue<1.5&&isRotated) ?this.offsetX: (isRotated ? 506 : 760) * moveScaleX; let maxOffsetY = (isRotated ? 760 : 506) * moveScaleY; //rk3588 // let maxOffsetX = (this.scaleValue<1.5&&isRotated) ?this.offsetX: (isRotated ? 1018 : 1524) * moveScaleX; // let maxOffsetY = (isRotated ? 1524 : 1018) * moveScaleY; //rk3568 //console.info(TAG,"maxOffsetX",maxOffsetX,"scale",this.scaleValue,"offsetx",this.offsetX) const clampedX = Math.max(-maxOffsetX, Math.min(maxOffsetX, this.offsetX)); const clampedY = Math.max(-maxOffsetY, Math.min(maxOffsetY, this.offsetY)); if (this.offsetX !== clampedX || this.offsetY !== clampedY) { animateTo({ duration: 100, curve: Curve.EaseOut }, () => { this.offsetX = clampedX; this.offsetY = clampedY; }); } } //上传照片 uploadPhoto=()=>{ } async aboutToAppear() { this.previewManager = new PreviewManager(getContext(this)); this.loadPhotos(); await this.createSubscriber(); await sleep(50) await this.connectCamera(); } aboutToDisappear(): void { this.disconnectCamera() if (this.previewManager) { this.previewManager.release(); this.previewManager = null } if (this.subscriber) { CommonEventManager.unsubscribe(this.subscriber); } this.releaseAllPixelMaps() if (this.commodityPixelMap) { this.commodityPixelMap.release(); this.commodityPixelMap = null; } } build() { Row() { Stack(){ if (this.commodityPixelMap) { if (this.selectedPhotoIndex === -1) { Image(this.commodityPixelMap) .width('100%') .height('100%') .objectFit(ImageFit.Fill) } } if(this.isCapturing){ Column() { Text('正在拍照中...') .fontSize($r('app.float.fontSize_30')) .margin({bottom:'10%'}) LoadingProgress() .color(Color.Blue) .width('25%') .width('25%') } .height('30%') .width('25%') .backgroundColor(Color.White) .borderRadius($r('app.float.virtualSize_16')) .justifyContent(FlexAlign.Center) } if(this.selectedPhotoIndex === -1){ Row() { Image( this.selectedFlashMode === 0 ? $r('app.media.process_flash_auto') : this.selectedFlashMode === 1 ? $r('app.media.process_flash_open') : $r('app.media.process_flash_close') ) .width($r('app.float.virtualSize_48')) .height($r('app.float.virtualSize_48')) .enabled(!this.isCapturing) .onClick(() => { this.isExpanded = !this.isExpanded }) .margin({right:'5%'}) if (this.isExpanded) { Row() { Row(){ Text('自动') .fontSize($r('app.float.fontSize_24')) .fontColor(this.selectedFlashMode===0?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF')) } .width('33.3%') .height('100%') .justifyContent(FlexAlign.Center) .borderRadius($r('app.float.virtualSize_16')) .backgroundColor(this.selectedFlashMode===0?$r('app.color.0A84FF'):$r('app.color.60000000')) .onClick(() => { this.setFlashMode('autoflash') this.isExpanded = false this.selectedFlashMode = 0 }) Row(){ Text('开启') .fontSize($r('app.float.fontSize_24')) .fontColor(this.selectedFlashMode===1?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF')) } .width('33.3%') .height('100%') .justifyContent(FlexAlign.Center) .borderRadius($r('app.float.virtualSize_16')) .backgroundColor(this.selectedFlashMode===1?$r('app.color.0A84FF'):$r('app.color.60000000')) .onClick(() => { this.setFlashMode('openflash') this.isExpanded = false this.selectedFlashMode = 1 }) Row(){ Text('关闭') .fontSize($r('app.float.fontSize_24')) .fontColor(this.selectedFlashMode===2?$r('app.color.FFFFFF'):$r('app.color.60FFFFFF')) } .width('33.3%') .height('100%') .justifyContent(FlexAlign.Center) .borderRadius($r('app.float.virtualSize_16')) .backgroundColor(this.selectedFlashMode===2?$r('app.color.0A84FF'):$r('app.color.60000000')) .onClick(() => { this.setFlashMode('closeflash') this.isExpanded = false this.selectedFlashMode = 2 }) } .animation({ duration: 300, curve: Curve.EaseOut }) .backgroundColor($r('app.color.60000000')) .borderRadius($r('app.float.virtualSize_16')) .width('85%') .height('100%') } } .justifyContent(FlexAlign.Start) .alignItems(VerticalAlign.Bottom) .position({ x: '2%', y: '92%' }) .height('5%') .width('24%') Image(this.shootButtonClick===1?$r('app.media.process_no_shoot'):$r('app.media.process_shoot')) .width($r('app.float.virtualSize_88')) .height($r('app.float.virtualSize_88')) .scale({ x: this.shootButtonClick, y: this.shootButtonClick }) .borderRadius($r('app.float.fontSize_16')) .enabled(!this.isCapturing) .animation({ duration: 200, curve: Curve.Linear }) .onClick(() => { this.shootButtonClick = 0.9; setTimeout(() => { this.shootButtonClick = 1; }, 200); this.takePhoto(); }) .position({ x: '48%', y: '90%' }) Row(){ Text("重连相机") .fontColor($r('app.color.FFFFFF')) .fontSize($r('app.float.fontSize_24')) } .width('10%') .height('6%') .backgroundColor($r('app.color.60000000')) .position({ x: '88%', y: '91%' }) .borderRadius($r('app.float.virtualSize_16')) .enabled(!this.isCapturing) .justifyContent(FlexAlign.Center) .onClick(()=>{ this.reconnectCamera() }) } else{ Stack() { Image(this.photoPixelMaps[this.selectedPhotoIndex].pixelMap) .width('100%') .height('100%') .autoResize(true) // .onComplete((event) => { // console.info(TAG,"width:"+event!.componentWidth +"height:"+ event!.componentHeight) // }) // 获取Image组件的长宽(除以1.5获取vp) .objectFit(ImageFit.Fill) .rotate({ angle: this.rotateAngle }) .scale({ x: this.scaleValue, y: this.scaleValue }) .borderRadius($r('app.float.virtualSize_16')) .translate({ x: this.offsetX, y: this.offsetY }) .gesture( GestureGroup(GestureMode.Exclusive, PinchGesture() .onActionStart((event: GestureEvent) => { this.lastScale = this.scaleValue; this.lastOffsetX = this.offsetX; this.lastOffsetY = this.offsetY; // 记录双指中心点(相对于图片中心) this.pinchCenterX = event.pinchCenterX - 380 - 760 / 2;//rk3588 this.pinchCenterY = event.pinchCenterY - 130 - 506 / 2;//rk3588 // this.pinchCenterX = event.pinchCenterX - 1016 / 2; //rk3568 // this.pinchCenterY = event.pinchCenterY - 678 / 2; //rk3568 // console.info(TAG,this.pinchCenterX,TAG,this.pinchCenterY) }) .onActionUpdate((event: GestureEvent) => { let newScale = this.lastScale * event.scale; //保持双指中心点不变 const scaleRatio = newScale / this.lastScale; const newOffsetX = this.lastOffsetX + (1 - scaleRatio) * this.pinchCenterX; const newOffsetY = this.lastOffsetY + (1 - scaleRatio) * this.pinchCenterY; this.offsetX = newOffsetX; this.offsetY = newOffsetY; this.scaleValue = newScale; }) .onActionEnd(() => { // 缩放最小比例为1 if (this.scaleValue < 1) { // 旋转90°或者270°的最小缩小比例为0.667 if(this.rotateAngle==90||this.rotateAngle==270) { if(this.scaleValue<0.667){ this.scaleValue = 0.667 } } else { this.scaleValue = 1 } } // 缩放最大比例为3 if (this.scaleValue > 3) this.scaleValue = 3 this.adjustOffsetWithAnimation() }), // 单指滑动手势 PanGesture() .onActionStart(() => { if(this.rotateAngle === 90 || this.rotateAngle === 270) { if (this.scaleValue <= 0.667) { return; } }else{ if (this.scaleValue <= 1) return; } this.lastOffsetX = this.offsetX; this.lastOffsetY = this.offsetY; }) .onActionUpdate((event: GestureEvent) => { if(this.rotateAngle === 90 || this.rotateAngle === 270) { if (this.scaleValue <= 0.667) { return; } }else{ if (this.scaleValue <= 1) return; } let dx = event.offsetX; let dy = event.offsetY; const sensitivity = 0.5 * this.scaleValue; // 临时计算新位置 let newOffsetX = this.lastOffsetX; let newOffsetY = this.lastOffsetY; newOffsetX += dx * sensitivity; newOffsetY += dy * sensitivity; this.offsetX = newOffsetX; this.offsetY = newOffsetY; //console.info(TAG,this.offsetX,TAG,this.offsetY) }) .onActionEnd(() => { const isRotated = (this.rotateAngle === 90 || this.rotateAngle === 270); if(isRotated) { if(this.scaleValue<=1.5) { this.offsetX = 0 } }else{ if (this.scaleValue <= 1) { // 如果缩放比例<=1,直接重置位置 this.offsetX = 0; this.offsetY = 0; return; } } this.adjustOffsetWithAnimation() }) ) ) } .width('100%') .height('100%') .borderRadius($r('app.float.virtualSize_16')) .clip(true) Row() { Row(){ Image($r('app.media.process_back_camera')) .width($r('app.float.virtualSize_80')) .height($r('app.float.virtualSize_80')) .onClick(()=>{ this.selectedPhotoIndex=-1 this.startView() }) } .width('10%') .justifyContent(FlexAlign.Start) Row({space:20}){ Image($r("app.media.process_photo_reset")) .width($r('app.float.virtualSize_80')) .height($r('app.float.virtualSize_80')) .onClick(()=>{ this.resetImageTransform() }) Image($r('app.media.process_photo_turn_left')) .width($r('app.float.virtualSize_80')) .height($r('app.float.virtualSize_80')) .onClick(()=>{ this.rotateImage(-90) }) Image($r('app.media.process_photo_turn_right')) .width($r('app.float.virtualSize_80')) .height($r('app.float.virtualSize_80')) .onClick(()=>{ this.rotateImage(90) }) Image($r('app.media.process_photo_delete')) .width($r('app.float.virtualSize_80')) .height($r('app.float.virtualSize_80')) .onClick(()=>{ this.showConfirmDialog({ title: '删除照片', message: `确定要删除照片吗?`, onConfirm: ()=> { this.deletePhoto(this.selectedPhotoIndex); } }); }) }.width('88%') .justifyContent(FlexAlign.End) .margin({right :'2%'}) }.width('98%') .height('10%') .position({x:'2%',y:'90%'}) } } .width('86%') .height('100%') .backgroundColor($r('app.color.000000')) .border({width:2}) Column(){ List({ space: 8,scroller:this.scrollerPhoto }) { ForEach(this.photoPixelMaps, (item:photoInfo, index) => { ListItem() { Column({space:4}){ Column(){ Image(item.pixelMap) .objectFit(ImageFit.Fill) .borderRadius($r('app.float.virtualSize_16')) .height('97%') .width('98%') .opacity(index === this.selectedPhotoIndex ? 0.8 : 1) // 20% 透明度 Text(item.timestamp) .fontSize($r('app.float.fontSize_12')) .fontColor($r('app.color.FFFFFF')) .width('100%') .textAlign(TextAlign.Start) } .backgroundColor(index === this.selectedPhotoIndex ? $r('app.color.30D158') : '') .width('90%') .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) .borderRadius($r('app.float.virtualSize_16')) .height('85%') // Text(`${item.updated}`) // .fontSize($r('app.float.fontSize_12')) // .fontColor($r('app.color.FFFFFF')) // .width('90%') // .textAlign(TextAlign.Start) } .width('100%') .height('100%') .justifyContent(FlexAlign.Start) .alignItems(HorizontalAlign.Center) .enabled(!this.isCapturing) .onClick(()=>{ this.selectedPhotoIndex = index; this.resetImageTransform() this.stopView(); }) }.height('19%') .margin({bottom:'2%'}) }) } .width('100%') .margin({top:'2%',bottom:'2%'}) .height('96%') } .width('14%') .height('100%') } .width('100%') .height('100%') .backgroundColor($r('app.color.10FFFFFF')) .borderRadius($r('app.float.virtualSize_16')) .borderWidth(4) } } function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } interface photoInfo { pixelMap: PixelMap; timestamp: string; filename: string; }