HarmonyOS鸿蒙开发实战(5.0)悬浮窗拖拽和吸附动画实践

embedded/2024/9/24 10:24:17/

鸿蒙HarmonyOS NEXT开发实战往期文章必看(持续更新......)

HarmonyOS NEXT应用开发性能实践总结

HarmonyOS NEXT应用开发案例实践总结合集

最新版!“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)

非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线!


介绍

本示例使用position绝对定位实现应用内悬浮窗,并且通过animateTo结合curves动画曲线实现悬浮窗拖拽跟手和松手吸附边缘的弹性动画效果。

效果图预览

使用说明

按住悬浮窗可以拖拽,松开后悬浮窗自动靠左或靠右,如果悬浮窗超出内容区上下边界,自动吸附在边界位置。

实现思路

  1. 悬浮窗组件使用Stack嵌套video布局,使用属性position绝对定位使组件悬浮。源码参考FloatWindowMainPage.ets
Stack({ alignContent: Alignment.Bottom }) {Video({src: $rawfile('float_window_video.mp4'),controller: this.videoController}).controls(false).autoPlay(true).loop(true).muted(true).width($r('app.string.float_window_full_size')).onClick(() => {this.videoController.requestFullscreen(true);}).borderRadius($r('app.integer.float_window_content_border_radius'))Text($r('app.string.float_window_live_text')).width($r('app.string.float_window_full_size')).fontSize($r('app.string.ohos_id_text_size_body1')).fontColor($r('app.color.ohos_id_color_background')).textAlign(TextAlign.Center).backgroundColor($r('app.color.ohos_id_color_list_alert')).borderRadius({bottomLeft: $r('app.integer.float_window_content_border_radius'),bottomRight: $r('app.integer.float_window_content_border_radius')})
}
.clip(true)
.border({width: $r('app.integer.float_window_border_width'),color: $r('app.color.ohos_id_color_background')
})
.borderRadius($r('app.string.ohos_id_corner_radius_default_l'))
.width(Constants.FLOAT_WINDOW_WIDTH)
.height(Constants.FLOAT_WINDOW_HEIGHT)
.backgroundColor($r('app.color.ohos_id_color_foreground'))
.position(this.edge)
.onTouch((event: TouchEvent) => {this.onTouchEvent(event);
})
  1. 初始化时悬浮窗的position属性设置top和right,让悬浮窗靠右。源码参考FloatWindowMainPage.ets
  @State edge: Edges = { top: Constants.INIT_POSITION_Y, right: Constants.PAGE_PADDING };
  1. 父组件添加onAreaChange回调,获取父组件的宽高。源码参考FloatWindowMainPage.ets
  // 父组件宽度@State containerWidth: number = 0;// 父组件高度@State containerHeight: number = 0;.onAreaChange((oldValue: Area, newValue: Area) => {// TODO:性能知识点:onAreaChange是高频回调,仅在父组件尺寸改变时获取新的父组件宽高,避免性能损耗if (oldValue.width !== newValue.width) {this.containerWidth = newValue.width as number;}if (oldValue.height !== newValue.height) {this.containerHeight = newValue.height as number;}})
  1. 悬浮窗组件添加onTouchEvent回调,在手指按下时保存触摸点在窗口中的坐标,用于移动时悬浮窗位置的计算。源码参考FloatWindowMainPage.ets
  // 拖拽移动开始时悬浮窗在窗口中的坐标,每次移动回调触发时更新private windowStartX: number = 0;private windowStartY: number = 0;case TouchType.Down: {// 获取拖拽开始时悬浮窗在窗口中的坐标this.windowStartX = event.touches[0].windowX;this.windowStartY = event.touches[0].windowY;break;}
  1. 手指移动时,获取触摸点相对于应用窗口左上角的X和Y坐标,通过计算设置悬浮窗的position坐标实现拖拽,使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion结合animateTo实现跟手动画效果。源码参考FloatWindowMainPage.ets
  case TouchType.Move: {const windowX: number = event.touches[0].windowX;const windowY: number = event.touches[0].windowY;// TODO:知识点:跟手动画,推荐使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion。animateTo({ curve: curves.responsiveSpringMotion() }, () => {// 判断当前edge中属性left和right哪个不为undefined,用于控制悬浮窗水平方向的位置if (this.edge.left !== undefined) {this.edge.left = this.edge.left as number + (windowX - this.windowStartX);} else {this.edge.right = this.edge.right as number - (windowX - this.windowStartX);}this.edge.top = this.edge.top as number + (windowY - this.windowStartY);this.windowStartX = windowX;this.windowStartY = windowY;})break;}
  1. 手指抬起时,通过判断悬浮窗中心在水平方向位于窗口中心的左侧或右侧设置悬浮窗靠左或靠右,如果悬浮窗超出内容区上下边界,则将悬浮窗设置在边界位置,使用curves.springMotion弹性动画曲线实现吸附边界时的弹性动画效果。源码参考FloatWindowMainPage.ets
  case TouchType.Up: {// 计算悬浮窗中心点在父组件中水平方向的坐标let centerX: number;if (this.edge.left !== undefined) {centerX = this.edge.left as number + Constants.FLOAT_WINDOW_WIDTH / 2;}else{centerX = this.containerWidth - (this.edge.right as number) - Constants.FLOAT_WINDOW_WIDTH / 2;}// TODO:知识点:通过判断悬浮窗在父组件中的位置,设置悬浮窗贴边,使用curves.springMotion()弹性动画曲线,可以实现阻尼动画效果animateTo({ curve: curves.springMotion() }, () => {// 判断悬浮窗中心在水平方向是否超过父组件宽度的一半,根据结果设置靠左或靠右if (centerX > (this.containerWidth / 2)) {this.edge.right = Constants.PAGE_PADDING;this.edge.left = undefined;} else {this.edge.right = undefined;this.edge.left = Constants.PAGE_PADDING;}// 判断悬浮窗是否超出内容区上下边界,根据结果将悬浮窗设置在边界位置if (this.edge.top as number < Constants.PAGE_PADDING) {this.edge.top = Constants.PAGE_PADDING;} else if (this.edge.top as number > this.containerHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING) {this.edge.top = this.containerHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING;}})break;}

高性能知识点

不涉及

工程结构&模块类型

floatwindow                                  // har类型
|---/src/main/ets/common                        
|   |---Constants.ets                        // 常量
|---/src/main/ets/pages                        
|   |---FloatWindowMainPage.ets              // 视图层-悬浮窗首页

模块依赖

  1. 本示例依赖动态路由模块来实现页面的动态加载。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为体系杂乱无章,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)路线、视频、文档用来跟着学习是非常有必要的。

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员

鸿蒙 NEXT 全栈开发学习笔记 希望这一份鸿蒙学习文档能够给大家带来帮助~

这份鸿蒙(HarmonyOS NEXT)包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频教程+学习PDF文档

鸿蒙语法ArkTS、TypeScript、ArkUI教程……)全球开发者的开源社区,开源代码

 纯血版鸿蒙全套学习文档(面试、文档、全套视频等)全球开发者的开源社区,开源代码

                   

鸿蒙APP开发必备

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线


http://www.ppmy.cn/embedded/116049.html

相关文章

Redis配置文件详解

1.Units单位 配置大小单位,开头定义了一些基本的度量单位&#xff0c;只支持 bytes&#xff0c;不支持 bit 大小写不敏感 # 1k > 1000 bytes # 1kb > 1024 bytes # 1m > 1000000 bytes # 1mb > 1024*1024 bytes # 1g > 1000000000 bytes # 1gb > 1024*102…

【C++指南】inline内联函数详解

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 目录 引言 C为什么引入了inline来替代C语言中的宏 inline的基本用法 定义inline函数 inline的优势与…

kotlin—— withTimeoutOrNull的介绍和使用场景

withTimeoutOrNull 是 Kotlin 协程库中的一个非常有用的函数&#xff0c;它允许你在指定的时间内执行一个代码块&#xff0c;如果在指定的时间内代码块没有执行完毕&#xff0c;那么它会返回 null&#xff0c;而不是抛出一个异常。这个函数在处理可能需要较长时间完成的操作&am…

在 macOS 上安装 FFmpeg 的详细指南

FFmpeg 是一个强大的多媒体框架&#xff0c;可以用于录制、转换和流式传输音频和视频。无论你是视频编辑工作者&#xff0c;还是想要处理音频文件&#xff0c;FFmpeg 都是一个不可或缺的工具。本文将介绍在 macOS 上安装 FFmpeg 的几种方法。 方法 1: 使用 Homebrew 安装 FFmp…

【uni-app】小兔鲜项目-基础架构-请求和上传文件拦截器

注意事项 uni.request 请求封装 请求和上传文件拦截器 uniapp 拦截器&#xff1a; uni.addInterceptor 接口说明&#xff1a;接口文档 实现需求 拼接基础地址设置超时时间添加请求头标识添加 token 参考代码 // src/utils/http.ts// 请求基地址 const baseURL https://pca…

provide,inject父传子

这个方法传递子参数&#xff0c;可以让所有的子组件获取到&#xff0c;不能子组件传递给父组件 父组件 说下大概思路&#xff0c;导入privode&#xff0c;然后使用privode方法&#xff0c;有点像redis&#xff0c;key value形式存值&#xff0c;子组件可以通过key来获取你要传…

计算机毕业设计之:基于微信小程序的中药材科普系统(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Redis数据结构之哈希表

这里的哈希表说的是value的类型是哈希表 一.相关命令 1.hset key field value 一次可以设置多个 返回值是设置成功的个数 注意&#xff0c;哈希表中的键值对&#xff0c;键是唯一的而值可以重复 所以有下面的结果&#xff1a; key中原来已经有了f1&#xff0c;所以再使用hse…