鸿蒙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 …...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
