鸿蒙UI开发——自定义UI绘制帧率

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)    }  }}

效果如下:

相关推荐
不爱吃糖的程序媛13 小时前
OpenHarmony 工程结构剖析
harmonyos
I'm Jie16 小时前
Swagger UI 本地化部署,解决 FastAPI Swagger UI 依赖外部 CDN 加载失败问题
python·ui·fastapi·swagger·swagger ui
爱学习的程序媛17 小时前
【Web前端】优化Core Web Vitals提升用户体验
前端·ui·web·ux·用户体验
爱学习的程序媛18 小时前
【Web前端】前端用户体验优化全攻略
前端·ui·交互·web·ux·用户体验
紫丁香18 小时前
Selenium自动化测试详解1
python·selenium·测试工具·ui
GISer_Jing18 小时前
前端组件库——shadcn/ui:轻量、自由、可拥有,解锁前端组件库的AI时代未来
前端·人工智能·ui
小白学鸿蒙18 小时前
使用Flutter从0到1构建OpenHarmony/HarmonyOS应用
flutter·华为·harmonyos
HarmonyOS_SDK20 小时前
接口高效调用,实现应用内无感促评
harmonyos
没头脑的男大20 小时前
华为题目152乘积最大子数组
算法·华为
江澎涌21 小时前
鸿蒙动态导入实战
android·typescript·harmonyos