
1、概 述
随着设备屏幕的不断演进,当前主流设备采用LTPO屏幕(可变刷新率屏幕),此类屏幕支持在多个档位之间切换屏幕帧率。
对于快速变化的内容,如射击游戏,交互动画等,显示帧率越高,画面越流畅,但是相对的功耗也会越高。
而低速变化的内容,如游戏大厅,时钟更新动画等,画面更新频率较低,使用相对低的显示帧率,用户也不会觉得卡顿,但是相对的功耗就比较低。
基于显示内容的可变帧率能力,在具备LTPO屏幕的设备上,可以达到性能体验和功耗间的平衡。
HarmonyOS支持可变帧率能力,我们通过使用可变帧率接口,进行相关业务开发,可以享受可变帧率特性带来的功耗收益。
可变帧率为应用开发中的动画组件、XComponent组件、UI绘制等提供一种基础帧率配置和能力。
通过设置有效的期望绘制帧率后,系统会收集设置的请求帧率,进行决策和分发,在渲染管线上进行分频,尽量能够满足调用方的期望帧率。
【在我们自己希望独立绘制渲染一些内容时(例如使用Canvas自定义一些动态效果),也可以考虑用这种方式】
2、配置自定义UI绘制帧率
如果我们需要以独立的帧率绘制更新操作UI界面时,可以通过DisplaySync来实现。
模块如下:
import { displaySync } from '@kit.ArkGraphics2D';
创建一个DisplaySync对象方法如下:
let backDisplaySync: displaySync.DisplaySync = displaySync.create();
DisplaySync对象定义如下:
class DisplaySync { // 设置期望的帧率范围。 setExpectedFrameRateRange(rateRange: ExpectedFrameRateRange) : void // 订阅每一帧变化 on(type: 'frame', callback: Callback<IntervalInfo>): void // 取消订阅每一帧的变化 off(type: 'frame', callback?: Callback<IntervalInfo>): void // 开始每帧回调 start(): void // 停止每帧回调 stop(): void}// 设置帧率范围的入参结构如下class ExpectedFrameRateRange { min: number, // 期望的最小帧率。 max: number, // 期望的最大帧率。 expected: number, // 期望的最优帧率。}
此处以不同帧率改变文件组件字体大小为例,来模拟不同UI绘制帧率的效果。步骤如下:
**👉🏻 step 1:导入模块并定义DisplaySync对象。**
import { displaySync } from '@kit.ArkGraphics2D';@Entry@Componentstruct Index { // 定义两个DisplaySync变量(slow是30帧,fast是60帧),未初始化 private backDisplaySyncSlow: displaySync.DisplaySync | undefined = undefined; private backDisplaySyncFast: displaySync.DisplaySync | undefined = undefined;}
**👉🏻 step 2:定义两个文本组件。**
@State drawFirstSize: number = 25;@State drawSecondSize: number = 25;@Builder doSomeRenderFirst() { Text('30') .fontSize(this.drawFirstSize)}@Builder doSomeRenderSecond() { Text('60') .fontSize(this.drawSecondSize)}
**👉🏻 step 3:**通过DisplaySync实例设置帧率和注册订阅函数。****
CreateDisplaySyncSlow() { let range : ExpectedFrameRateRange = { // 创建和配置帧率参数 expected: 30, // 设置期望绘制帧率为30hz min: 0, // 配置帧率范围 max: 120 // 配置帧率范围 }; let draw30 = (intervalInfo: displaySync.IntervalInfo) => { // 订阅回调函数,字体大小在25到150之间变化 if (this.isBigger_30) { this.drawFirstSize += 1; if (this.drawFirstSize > 150) { this.isBigger_30 = false; } } else { this.drawFirstSize -= 1; if (this.drawFirstSize < 25) { this.isBigger_30 = true; } } };
this.backDisplaySyncSlow = displaySync.create(); // 创建DisplaySync实例 this.backDisplaySyncSlow.setExpectedFrameRateRange(range); // 设置帧率 this.backDisplaySyncSlow.on("frame", draw30); // 订阅frame事件和注册订阅函数}
**👉🏻 step 4:**开始每帧回调****
Button('Start') .id('CustomDrawStart') .fontSize(14) .fontWeight(500) .margin({ bottom: 10, left: 5 }) .fontColor(Color.White) .onClick((): void => { if (this.backDisplaySyncSlow == undefined) { this.CreateDisplaySyncSlow(); } if (this.backDisplaySyncFast == undefined) { this.CreateDisplaySyncFast(); } if (this.backDisplaySyncSlow) { this.backDisplaySyncSlow.start(); } if (this.backDisplaySyncFast) { this.backDisplaySyncFast.start(); } }) .width('20%') .height(40) .shadow(ShadowStyle.OUTER_DEFAULT_LG)
👉🏻 step 5:结束每帧回调****
Button('Stop') .id('CustomDrawStop') .fontSize(14) .fontWeight(500) .margin({ bottom: 10, left: 5 }) .fontColor(Color.White) .onClick((): void => { if (this.backDisplaySyncSlow) { this.backDisplaySyncSlow.stop(); } if (this.backDisplaySyncFast) { this.backDisplaySyncFast.stop(); } }) .width('20%') .height(40) .shadow(ShadowStyle.OUTER_DEFAULT_LG)
3、一个完整实例
import { displaySync } from '@kit.ArkGraphics2D';@Entry@Componentstruct Index { @State drawFirstSize: number = 25; @State drawSecondSize: number = 25; private backDisplaySyncSlow: displaySync.DisplaySync | undefined = undefined; private backDisplaySyncFast: displaySync.DisplaySync | undefined = undefined; private isBigger_30:boolean = true; private isBigger_60:boolean = true; @Builder doSomeRenderFirst() { Text('30') .fontSize(this.drawFirstSize) } @Builder doSomeRenderSecond() { Text('60') .fontSize(this.drawSecondSize) } CreateDisplaySyncSlow() { // 定义期望绘制帧率 let range : ExpectedFrameRateRange = { expected: 30, min: 0, max: 120 }; let draw30 = (intervalInfo: displaySync.IntervalInfo) => { if (this.isBigger_30) { this.drawFirstSize += 1; if (this.drawFirstSize > 150) { this.isBigger_30 = false; } } else { this.drawFirstSize -= 1; if (this.drawFirstSize < 25) { this.isBigger_30 = true; } } }; this.backDisplaySyncSlow = displaySync.create(); // 创建DisplaySync实例 this.backDisplaySyncSlow.setExpectedFrameRateRange(range); // 设置帧率 this.backDisplaySyncSlow.on("frame", draw30); // 订阅frame事件和注册订阅函数 } CreateDisplaySyncFast() { // 定义期望绘制帧率 let range : ExpectedFrameRateRange = { expected: 60, min: 0, max: 120 }; let draw60 = (intervalInfo: displaySync.IntervalInfo) => { if (this.isBigger_60) { this.drawSecondSize += 1; if (this.drawSecondSize > 150) { this.isBigger_60 = false; } } else { this.drawSecondSize -= 1; if (this.drawSecondSize < 25) { this.isBigger_60 = true; } } }; this.backDisplaySyncFast= displaySync.create(); // 创建DisplaySync实例 this.backDisplaySyncFast.setExpectedFrameRateRange(range); // 设置帧率 this.backDisplaySyncFast.on("frame", draw60); // 订阅frame事件和注册订阅函数 } aboutToDisappear() { if (this.backDisplaySyncSlow) { this.backDisplaySyncSlow.stop(); // DisplaySync失能关闭 this.backDisplaySyncSlow = undefined; // 实例置空 } if (this.backDisplaySyncFast) { this.backDisplaySyncFast.stop(); // DisplaySync失能关闭 this.backDisplaySyncFast = undefined; // 实例置空 } } build() { Column() { Row() { this.doSomeRenderFirst(); } .height('40%') Row() { this.doSomeRenderSecond(); } .height('40%') Row() { Button('Start') .id('CustomDrawStart') .fontSize(14) .fontWeight(500) .margin({ bottom: 10, left: 5 }) .fontColor(Color.White) .onClick((): void => { if (this.backDisplaySyncSlow == undefined) { this.CreateDisplaySyncSlow(); } if (this.backDisplaySyncFast == undefined) { this.CreateDisplaySyncFast(); } if (this.backDisplaySyncSlow) { this.backDisplaySyncSlow.start(); // DisplaySync使能开启 } if (this.backDisplaySyncFast) { this.backDisplaySyncFast.start(); // DisplaySync使能开启 } }) .width('20%') .height(40) .shadow(ShadowStyle.OUTER_DEFAULT_LG) Button('Stop') .id('CustomDrawStop') .fontSize(14) .fontWeight(500) .margin({ bottom: 10, left: 5 }) .fontColor(Color.White) .onClick((): void => { if (this.backDisplaySyncSlow) { this.backDisplaySyncSlow.stop(); // DisplaySync失能关闭 } if (this.backDisplaySyncFast) { this.backDisplaySyncFast.stop(); // DisplaySync失能关闭 } }) .width('20%') .height(40) .shadow(ShadowStyle.OUTER_DEFAULT_LG) } .width('100%') .justifyContent(FlexAlign.Center) .shadow(ShadowStyle.OUTER_DEFAULT_SM) .alignItems(VerticalAlign.Bottom) .layoutWeight(1) } }}
效果如下:
