鸿蒙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 …...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
MySQL的pymysql操作
本章是MySQL的最后一章,MySQL到此完结,下一站Hadoop!!! 这章很简单,完整代码在最后,详细讲解之前python课程里面也有,感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
