鸿蒙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
@Component
struct 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
@Component
struct 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 …...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...

RabbitMQ work模型
Work 模型是 RabbitMQ 最基础的消息处理模式,核心思想是 多个消费者竞争消费同一个队列中的消息,适用于任务分发和负载均衡场景。同一个消息只会被一个消费者处理。 当一个消息队列绑定了多个消费者,每个消息消费的个数都是平摊的&a…...