HarmonyOS APP实战-画图APP - 第14篇:可变帧率与性能优化
HarmonyOS APP实战-画图APP - 第14篇:使用displaySync定制绘制帧率
开篇
上一篇我们完成了本地数据库存储与读取功能:通过关系型数据库(RdbStore)保存了画作元数据(名称、创建时间、缩略图路径),并实现了画作列表页面,支持从列表加载已有画作继续编辑。数据库模块是画图APP的数据持久化基石。
本篇将聚焦性能优化——根据绘制内容的复杂度动态调整绘制帧率。画图APP在画笔手绘时需要高帧率(60fps)保证跟手性;而空闲状态或显示静态图形时,可以降低帧率至30fps以减少功耗。HarmonyOS提供了displaySyncAPI,允许开发者针对自绘制内容指定不同的目标帧率。我们将封装一个FrameRateController模块,并在DrawingCanvas组件中集成它,实现按需切换帧率。
核心实现
基础配置:导入与常量定义
首先,在模块顶部导入displaySync,并定义帧率常量。
// FrameRateController.etsimport{displaySync}from'@ohos.graphics.displaySync';/** * 帧率常量 */exportconstFRAME_RATE_HIGH=60;// 高帧率模式(画笔绘制时)exportconstFRAME_RATE_LOW=30;// 低帧率模式(静止或浏览时)关键点说明
@ohos.graphics.displaySync是ArkGraphics 2D提供的可变帧率能力模块。- 帧率值单位是fps(帧每秒),60fps适合手绘追踪,30fps满足静态显示且降低功耗。
核心逻辑:FrameRateController类
FrameRateController负责创建和管理DisplaySync实例,并根据业务状态设置目标帧率。
// FrameRateController.etsexportclassFrameRateController{privatedisplaySyncInstance:displaySync.DisplaySync|null=null;privateisRunning:boolean=false;privatecurrentRate:number=FRAME_RATE_LOW;/** * 初始化DisplaySync实例 * @param requestId 请求标识,用于区分不同绘制场景 */publicasyncinit(requestId:string):Promise<void>{if(this.displaySyncInstance){return;}try{this.displaySyncInstance=awaitdisplaySync.create(requestId);console.info(`FrameRateController: DisplaySync created with requestId=${requestId}`);}catch(err){console.error(`FrameRateController: create failed, error=${JSON.stringify(err)}`);}}/** * 启动帧率控制 * @param frameCallback 每帧回调,参数为时间戳 */publicstart(frameCallback:(timestamp:number)=>void):void{if(!this.displaySyncInstance||this.isRunning){return;}this.displaySyncInstance.start(frameCallback);this.isRunning=true;console.info('FrameRateController: started');}/** * 停止帧率控制 */publicstop():void{if(!this.displaySyncInstance||!this.isRunning){return;}this.displaySyncInstance.stop();this.isRunning=false;console.info('FrameRateController: stopped');}/** * 更新目标帧率 * @param rate 新的帧率值(fps) */publicupdateFrameRate(rate:number):void{if(!this.displaySyncInstance){console.warn('FrameRateController: instance not initialized');return;}try{this.displaySyncInstance.setExpectedFrameRate(rate);this.currentRate=rate;console.info(`FrameRateController: frame rate updated to${rate}fps`);}catch(err){console.error(`FrameRateController: setExpectedFrameRate failed, error=${JSON.stringify(err)}`);}}/** * 销毁DisplaySync实例 */publicdestroy():void{if(this.displaySyncInstance){this.displaySyncInstance.destroy();this.displaySyncInstance=null;this.isRunning=false;console.info('FrameRateController: destroyed');}}/** * 获取当前帧率 */publicgetCurrentRate():number{returnthis.currentRate;}}关键点说明
displaySync.create(requestId)是一个异步方法,返回Promise<DisplaySync>实例。setExpectedFrameRate(rate)设置期望帧率,系统会尽量按照该值调度。start(callback)开始帧率控制,回调参数为时间戳(纳秒),可用于驱动动画循环。destroy()释放资源,在页面销毁时调用。- 实际项目中,帧率回调可以作为绘制驱动的定时器,替代
setInterval或requestAnimationFrame,提供更精确的帧同步。
完整模块:集成到DrawingCanvas组件
DrawingCanvas是画图APP的核心绘制组件。我们在其中集成FrameRateController,在触摸开始/移动时切换到高帧率,触摸结束或闲置时降回低帧率。同时,为了兼容原有绘制逻辑,我们将帧率回调绑定到画布重绘操作。
// DrawingCanvas.ets(简化版,仅展示集成逻辑)import{CanvasRenderingContext2D}from'@ohos.graphics.ui';import{FrameRateController,FRAME_RATE_HIGH,FRAME_RATE_LOW}from'./FrameRateController';import{Point,DrawCommand}from'../model/DrawCommand';// 假设自定义模型@Componentexportstruct DrawingCanvas{privatesettings:RenderingContextSettings=newRenderingContextSettings(true);privatecontext:CanvasRenderingContext2D=newCanvasRenderingContext2D(this.settings);privateframeCtrl:FrameRateController=newFrameRateController();privateisDrawing:boolean=false;privatelastTouch:Point={x:0,y:0};// ... 其他属性aboutToAppear(){// 初始化DisplaySync,requestId可自定义this.frameCtrl.init('drawing_canvas_main').then(()=>{// 启动帧率控制,将回调连接到画布刷新this.frameCtrl.start((timestamp:number)=>{// 这里可以驱动持续的绘制更新(例如动画)// 对于手绘,我们只在触摸事件中局部重绘,此回调暂不开启批量重绘// 但保持帧率控制活跃});// 初始设为低帧率this.frameCtrl.updateFrameRate(FRAME_RATE_LOW);});}onTouch(event:TouchEvent){if(!this.frameCtrl)return;switch(event.type){caseTouchType.Down:this.isDrawing=true;// 触摸开始时切换到高帧率,保证跟手this.frameCtrl.updateFrameRate(FRAME_RATE_HIGH);this.lastTouch.x=event.touches[0].x;this.lastTouch.y=event.touches[0].y;// 开始新路径this.startNewPath(this.lastTouch);break;caseTouchType.Move:if(this.isDrawing){constcurrent={x:event.touches[0].x,y:event.touches[0].y};// 添加点到当前路径并绘制this.addPointToPath(current);this.lastTouch=current;}break;caseTouchType.Up:caseTouchType.Cancel:// 绘制结束,恢复低帧率if(this.isDrawing){this.finishPath();this.isDrawing=false;this.frameCtrl.updateFrameRate(FRAME_RATE_LOW);}break;}}privatestartNewPath(point:Point):void{// 原有逻辑:开始一个新的Path}privateaddPointToPath(point:Point):void{// 原有逻辑:向Path增加点并调用context.drawPath()}privatefinishPath():void{// 原有逻辑:结束路径并保存到历史}aboutToDisappear(){this.frameCtrl.stop();this.frameCtrl.destroy();}build(){Canvas(this.context).width('100%').height('100%').onTouch((event:TouchEvent)=>this.onTouch(event))}}议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=placeholder.png&pos_id=img-K54aM3fo-1783906018434)
关键点说明
aboutToAppear中初始化FrameRateController并启动,默认低帧率。- 在
onTouch的Down事件中更新为高帧率,Up或Cancel时恢复低帧率。 - 注意:帧率回调本身不涉及重绘,但保持
start可以让系统按照目标帧率调度;实际绘制仍由触摸事件触发。 aboutToDisappear中停止并销毁实例,防止内存泄漏。- 如果后续需要实现动画绘制(如动态渐变、粒子效果),可以利用
frameCallback的时间戳驱动逐帧重绘,此时帧率控制效益更明显。
运行验证
在画图APP中,打开任意画布文件或新建画布,用手指在画布上绘制:
- 当手指按下并移动时,CPU/GPU会按60fps渲染轨迹,体验流畅跟手。
- 当手指抬起后,帧率会降为30fps,此时进行菜单操作或查看静态内容,手机功耗降低。
开发者可以通过DevEco Studio的Profiler工具查看帧率变化:
- 运行APP,点击“性能分析”
- 在“帧率时间线”中观察:绘制时段帧率约60fps,闲置时段约30fps。
如果未看到切换,请确认displaySync已正确初始化和权限设置(无需额外权限)。另外,模拟器可能不完全支持帧率动态调节,建议在真机上测试。
小结与预告
本篇实现了画图APP的帧率动态调节功能:通过FrameRateController模块封装displaySyncAPI,根据绘制活动自动切换60fps和30fps,在保持手绘跟手性的同时减少闲置功耗。该模块是性能优化的重要一环,可直接复用至其他自绘制场景。
下一篇《使用overdraw工具检测性能》将介绍如何利用HarmonyOS的过度绘制调试工具(Overdraw Debug)分析画布绘制效率,识别冗余绘制层次,并通过Canvas的clip、clearRect等技术优化绘制指令,进一步提升流畅度。