鸿蒙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, // 设置期望绘制帧率为30hzmin: 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)}}}
效果如下:

相关文章:
鸿蒙UI开发——自定义UI绘制帧率
1、概 述 随着设备屏幕的不断演进,当前主流设备采用LTPO屏幕(可变刷新率屏幕),此类屏幕支持在多个档位之间切换屏幕帧率。 对于快速变化的内容,如射击游戏,交互动画等,显示帧率越高࿰…...
鸿蒙基本组件结构
组件结构 1. 认识基本的组件结构 ArkTS通过装饰器Component 和Entry 装饰 struct 关键字声明的数据结构,构成一个自定义组件 自定义组件中提供了一个build函数,开发者需要在函数内以链式调用的方式进行基本的UI描述,UI描述的方法请参考UI描述…...
柔性鞋材振动刀智能视觉裁切机市场报告:未来几年年复合增长率CAGR为5.4%
震动刀切割设备是一种利用振动刀片在各种非金属材料表面上切割的设备,振动刀切割机利用刀片高频振动和360度旋转,能保证每分钟上万次的振动频率,可在平面进行垂直切割,锋利裁剪。震动刀切割设备切割速度快,可以单层切割…...
【计算机网络】基础知识,常识应用知识
局域网使用的是广播技术,广域网使用的是点对点技术,使用的协议不同。局域网工作在数据链路层,可以不要网络层,不存在路由选择问题。1968年6月,世界上最早的计算机网络是ARPAnet服务原语:请求、指示、相应、…...
【Linux进程篇1】认识冯·诺依曼体系结构(引出进程详解)
--------------------------------------------------------------------------------------------------------------------------------- 每日鸡汤: 用这生命中的每一秒,给自己一个不后悔的未来。 -----------------------------------------------…...
使用iviewui组件库的坑
背景 使用view-design组件库的Input组件的时候,按照产品的要求,输入框中只能键入正整数。 使用效果 如果直接使用组件的type属性,设置类型为number时,乍一看没啥问题,但是当我们键入 小数点(.) 或者 e/E 后面没有跟任…...
高级sql使用技巧
窗口函数(Window Functions): 窗口函数可以在结果集的行之间进行计算,例如计算移动平均值、排名等。在使用时,可以使用 OVER() 语句来定义窗口。例如: sql SELECT employee_id,salary,AVG(salary) OVER (P…...
403 Request Entity Too Lager(请求体太大啦)
昨天收到 QA 的生产报障,说是测试环境的附件上传功能报了 403 的错误,错误信息:403 Request Entity Too Lager。我尝试复现问题,发现传个几兆的文件都费劲啊,一传一个失败。不用说,项目用到 ng 代理&#x…...
Flutter 正在切换成 Monorepo 和支持 workspaces
其实关于 Monorepo 和 workspaces 相关内容在之前《Dart 3.5 发布,全新 Dart Roadmap Update》 和 《Flutter 之 ftcon24usa 大会,创始人分享 Flutter 十年发展史》 就有简单提到过,而目前来说刚好看到 flaux 这个新进展,所以就再…...
小白初入Android_studio所遇到的坑以及怎么解决
1. 安装Android_studio 参考:Android Studio 安装配置教程 - Windows(详细版)-CSDN博客 Android Studio超级详细讲解下载、安装配置教程(建议收藏)_androidstudio-CSDN博客 想下旧版本的android_studio的地址(仅供参考…...
NetCore使用Aop和内存缓存对接口、方法进行数据缓存
通过Aop内存缓存对接口、方法进行缓存 源码地址https://gitee.com/wangbenchi66/nuget 1. nuget包引入 必须引入包 至少在2024.11.7以上 <PackageReference Include"WBC66.Cache.Core" Version"2024.11.7" />必须开启内存缓存 否则后续步骤无法正…...
playwright学习记录2--定位方式
快捷导航 定位方式:元素操作断言方式自动等待 定位方式: csspage.get_by_role() 通过显式和隐式可访问性属性进行定位。page.get_by_text() 按文本内容定位。page.get_by_label() 通过关联标签的文本定位表单控件。page.get_by_placeholder() 通过占位符…...
响应式网页设计--html
一,HTML 文档的基本结构 一个典型的 HTML 文档包含了几个主要部分,基本结构如下(本文以下出现的所有代码都可以套入下面示例进行测试): <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8&q…...
C#核心(8) 静态成员
前言 先前我们已经学习了成员变量以及成员属性。 静态成员对于在整个应用程序中共享数据和功能非常有用。它们可以用于跟踪全局状态、共享常量和实现单例模式等。但是需要注意的是,过度使用静态成员可能导致代码变得难以维护和测试,因此应谨慎使用。其…...
关于git使用的图文教程(包括基本使用,处理冲突问题等等)超详细
目录 用户签名,初始化git git提交流程图 提交到本地库 版本穿梭 分支操作 分支合并冲突 团队协作 github的使用 推送代码 克隆 拉取代码 团队协作冲突 团队协作之分支管理 推送分支到分支: 拉去远程库分支到本地库: 本地删除远程分支&am…...
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
在前端开发的广阔天地中,Axios 犹如一颗璀璨的明星,为我们与服务器之间的通信搭建起坚实的桥梁。其中,responseType 属性更是赋予了我们灵活处理服务器响应的强大能力。 一、Axios 的 responseType 属性值及示例 1.arraybuffer 当我们将 r…...
redis集群介绍
1. 节点(Node): • Redis集群中的单个Redis服务器实例。每个节点都运行一个Redis服务器进程,并维护自己的数据。 2. 分片(Sharding): • 将数据集分割成多个部分,并分布到不同的节点…...
JDK中常用的包有哪些?
1.java.lang 描述:包含Java语言的核心类,不需要显式导入。 常用类:Object、String、Math、System、Thread、Exception等。 2.java.util 描述:提供了集合框架、日期和时间功能、随机数生成、扫描和格式化等实用工具类。 常用类…...
校园官网练习---web
HTML: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>西安工商学院</title><…...
MySQL中指定字段的某个值排在前面
一 需求 如果我们想讲表中指定的字段的某一个值排序在最前面应该如何处理? 二 实现方式 方法 1、使用<>,xml中使用<![CDATA[跳过解析的特殊符号]]>或者<(小于符号)>(大于符号) ORDER …...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
41道Django高频题整理(附答案背诵版)
解释一下 Django 和 Tornado 的关系? Django和Tornado都是Python的web框架,但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。它遵循MVC设计,并强调代码复用。Django有…...
Python_day48随机函数与广播机制
在继续讲解模块消融前,先补充几个之前没提的基础概念 尤其需要搞懂张量的维度、以及计算后的维度,这对于你未来理解复杂的网络至关重要 一、 随机张量的生成 在深度学习中经常需要随机生成一些张量,比如权重的初始化,或者计算输入…...
leetcode238-除自身以外数组的乘积
leetcode 238 思路 可以在不使用除法的情况下,利用前缀积和后缀积来实现解答 前缀积:对每个位置,计算当前数字左侧的所有数字的乘积后缀积:对每个位置,计算当前数字右侧的所有数字的乘积 结合这两种思想࿰…...
