鸿蒙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 …...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
