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

效果如下:

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/11333.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

递归写斐波那契数

在思考一些C语言编程题的解法时我们经常会碰到的一种算法是递归&#xff0c;递归的字面意思是传递回归&#xff0c;会用例子来解释和运用。 递归 例&#xff1a;在控制台输出指定项数的斐波那契数 斐波那契数列数列是指&#xff1a;1,1,2,3,5,8,13,21,34......从第三项开始等…

手写JDK动态代理实现AOP

AOP底层&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff0c;面向切面编程&#xff09;在 Java 中的实现有多种方式&#xff0c;其中使用 JDK 动态代理和 CGLIB 代理较为常见。 当你的应用程序遵循面向接口编程的原则时&#xff0c;JDK 动态代理是一个自然的…

Gin框架

GoWeb框架 GIN框架 基于httprouter开发的Web框架 安装与使用 安装 下载并安装GIN go get -u github.com/gin-gonic/gin 示例 package mainimport ("github.com/gin-gonic/gin" )func main() {// 创建一个默认的路由引擎r : gin.Default()// GET&#xff1a;请…

nodejs - nodejs安装步骤

安装 NodeJS 1.下载 NodeJS下载官网&#xff1a;https://nodejs.cn/download/ 2.验证 下载后解压安装&#xff0c;运行如下命令验证安装是否成功&#xff1a; node -v npm -v3.查看默认存放位置 查看npm默认存放位置&#xff0c;运行命令如下&#xff1a; npm get prefix…

Spring Boot框架:计算机课程管理的工程认证之光

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于工程教育认证的计算机课程管理平台的开发全过程。通过分析基于工程教育认证的计算机课程管理平台管理的不足&#xff0c;创建了一个计算机管理基于工程教育认…

游戏设计:推箱子【easyx图形界面/c语言】

在之前写程序设计的大作业时&#xff0c;在哔哩哔哩上跟着一个视频的学习的成果【第一个练习的】 今天整理文件的时候看到的&#xff0c;就发出来一下【CSDN和B站都有详细教程】 不是大项目&#xff0c;只有两个界面 这个代码只有两百行不到&#xff0c;但通过这个把基本的运…

C++数学

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 数论&#xff1a;质数、最大公约数、菲蜀定理 组合数学汇总 计算几何 博弈论 曼哈顿距离与切比雪夫距离 红线是哈曼顿距离&#xff0c;绿线是切比雪夫距离。 二维曼哈顿距离转切比雪夫距离 曼哈顿距离&#xff1a;|…

如何安装VMWare Workstation 16虚拟机

1、到VMware官网下载安装包。 2、下一步。 3、勾选同意协议&#xff0c;下一步。 4、更换安装路径&#xff0c;下一步。 5、取消全部勾选&#xff0c;下一步。 6、下一步。 7、安装。 8、等待安装完成。 9、安装完成&#xff0c;启动软件。 10、输入许可证ZF3R0…

光流分析技术

光流分析技术是一种重要的计算机视觉和图像处理技术&#xff0c;它通过分析连续帧图像中像素点的运动轨迹和速度&#xff0c;来捕捉图像中物体的运动和相邻帧之间的位移信息。以下是对光流分析技术的详细介绍&#xff1a; 一、光流的基本概念 光流&#xff08;Optical Flow&am…

Bearer 和 Digest 两个区别

Bearer 和 Digest 是两种常见的身份验证机制,主要用于在网络通信中验证用户的身份,以下是它们之间的区别: 认证原理 Bearer:也称为承载令牌认证,其核心是使用一个令牌(Token)来代表用户的身份信息。用户在进行身份验证后,服务器会颁发一个令牌给客户端,客户端在后续…

H264三种RTP打包方式

1. 单一NALU模式 单一NALU模式 适用于小于MTU&#xff08;最大传输单元&#xff09;的NALU。这种模式下&#xff0c;一个RTP包包含一个完整的NALU。RTP头部之后紧跟着NALU头和NALU数据。 封装格式&#xff1a; RTP头 | NALU头 | NALU数据这种方式简单直接&#xff0c;但仅适…

亚马逊评论爬虫+数据分析

爬取评论 做分析首先得有数据&#xff0c;数据是核心&#xff0c;而且要准确&#xff01; 1、爬虫必要步骤&#xff0c;选好框架 2、开发所需数据 3、最后测试流程 这里我所选框架是seleniumrequest&#xff0c;很多人觉得selenium慢&#xff0c;确实不快&#xff0c;仅针对此…

批量缓存模版

批量缓存模版 缓存通常有两种使用方式&#xff0c;一种是Cache-Aside&#xff0c;一种是cache-through。也就是旁路缓存和缓存即数据源。 一般一种用于读&#xff0c;另一种用于读写。参考后台服务架构高性能设计之道。 最典型的Cache-Aside的样例&#xff1a; //读操作 da…

09 Oracle数据拯救:Flashback Technologies精细级数据恢复指南

文章目录 09 Oracle数据拯救&#xff1a;Flashback Technologies精细级数据恢复指南一、Flashback Technologies概览二、Flashback Query&#xff1a;查询过去的数据三、Flashback Table&#xff1a;恢复整个表四、Flashback Database&#xff1a;恢复整个数据库五、总结与最佳…

BIST(Built-in Self-Test,内建自测试)学习笔记

参考资料: 内建自测试&#xff08;Built-in Self-Test&#xff0c;简称BIST&#xff09;详解_built in self test-CSDN博客 芯片测试术语 &#xff0c;片内测试(BIST)&#xff0c;ATE测试-CSDN博客 可能是DFT最全面的介绍--BIST - 知乎 (zhihu.com) 汽车功能安全--TC3xx LB…

three.js 杂记

在Three.js中&#xff0c;Object3D是所有3D对象的基类&#xff0c;而Group是Object3D的一个子类。Group的目的是为了简化处理多个对象的集合。当你将对象添加到Group中时&#xff0c;它们会以一个单元格的形式被处理&#xff0c;参与Group的某些操作&#xff0c;例如位置更新、…

go函数传值是值传递?还是引用传递?slice案例加图解

先说下结论 Go语言中所有的传参都是值传递&#xff08;传值&#xff09;&#xff0c;都是一个副本&#xff0c;一个拷贝。 值语义类型&#xff1a;参数传递的时候&#xff0c;就是值拷贝&#xff0c;这样就在函数中就无法修改原内容数据。 基本类型&#xff1a;byte、int、bool…

穿越时空的全球时钟:一个实时多时区显示的网页应用

引言 在当今这个全球化时代&#xff0c;人们经常需要与世界各地的朋友、同事或客户进行沟通。然而&#xff0c;由于时差的存在&#xff0c;找到一个合适的沟通时间往往成为一大挑战。为了解决这一问题&#xff0c;我们开发了一个名为“全球时钟”的网页应用&#xff0c;它能够…

本地部署免费开源助手Ollama

Ollama 安装 安装ollama 官方网站&#xff1a;https://ollama.com/download 2. 安装成功 3. 运行模型 模型&#xff1a;https://ollama.com/library 运行&#xff1a; ollama run llama3.2:3b Mac 、Linux 版本安装类似。 Open-WebUI界面安装 openwebui官网&#xff1a;http…

three.js杂记

空间 - 位置变换&#xff1a; // 假设有一个Three.js的对象: object3D // 存储矩阵位置 const matrix object3D.matrix.clone(); const matrixArray matrix.toArray(); // 转换为数组 // 之后&#xff0c;当你需要恢复位置时 object3D.matrix.fromArray(matrixArray); …