HarmonyOS Next 悬浮窗拖拽和吸附动画

news2024/12/23 13:24:11

介绍

本示例使用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.string.ohos_id_corner_radius_default_l'))
  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.string.ohos_id_corner_radius_default_l'),
      bottomRight: $r('app.string.ohos_id_corner_radius_default_l')
    })
}
.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({ x: this.positionX, y: this.positionY })
.onTouch((event: TouchEvent) => {
  this.onTouchEvent(event);
})
  1. 在悬浮窗组件的aboutToAppear中获取应用窗口尺寸,使用窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右。源码参考FloatWindowMainPage.ets
  try {
    const properties = windowClass.getWindowProperties();
    // 获取应用窗口宽高
    this.windowRectWidth = px2vp(properties.windowRect.width);
    this.windowRectHeight = px2vp(properties.windowRect.height)
    // 窗口宽度减去悬浮窗宽度和右边距让悬浮窗初始靠右
    this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING;
  } catch (exception) {
    logger.error(TAG, 'Failed to obtain the window properties. Cause: ' + JSON.stringify(exception));
  }
  1. 使用getWindowAvoidArea获取顶部状态栏高度和底部导航栏高度。源码参考FloatWindowMainPage.ets
  try {
    const avoidArea = windowClass.getWindowAvoidArea(type);
    // 获取顶部状态栏高度
    this.topRectHeight = px2vp(avoidArea.topRect.height);
    // 获取底部导航栏高度
    this.bottomRectHeight = px2vp(avoidArea.bottomRect.height);
  } catch (exception) {
    logger.error(TAG, 'Failed to obtain the area. Cause:' + JSON.stringify(exception));
  }
  1. 悬浮窗组件添加onTouchEvent回调,在手指按下时保存触摸点与悬浮窗左上角的偏移量offsetX和offsetY,用于移动时悬浮窗位置的计算。源码参考FloatWindowMainPage.ets
  case TouchType.Down: {
    this.offsetX = event.touches[0].x;
    this.offsetY = event.touches[0].y;
    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() }, () => {
      this.positionX = windowX - this.offsetX - Constants.PAGE_PADDING;
      this.positionY = windowY - this.offsetY - this.topRectHeight - Constants.PAGE_PADDING; // 减去手指位置到悬浮窗左上角的y轴偏移和设备顶部状态栏高度
    })
    break;
  }
  1. 手指抬起时,通过判断悬浮窗中心在水平方向位于窗口中心的左侧或右侧设置悬浮窗靠左或靠右,如果悬浮窗超出内容区上下边界,则将悬浮窗设置在边界位置,使用curves.springMotion弹性动画曲线实现吸附边界时的弹性动画效果。源码参考FloatWindowMainPage.ets
  case TouchType.Up: {
    // TODO:知识点:通过判断悬浮窗在窗口中的位置,设置悬浮窗贴边,使用curves.springMotion()弹性动画曲线,可以实现阻尼动画效果
    animateTo({ curve: curves.springMotion() }, () => {
      // 判断悬浮窗中心在水平方向是否超过窗口宽度的一半,根据结果设置靠左或靠右
      if (this.positionX > (this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH) / 2) {
        this.positionX = this.windowRectWidth - Constants.FLOAT_WINDOW_WIDTH - Constants.PAGE_PADDING; // 悬浮窗靠右
      } else {
        this.positionX = Constants.PAGE_PADDING; // 悬浮窗靠左
      }
      // 页面高度
      const pageHeight: number = this.windowRectHeight - this.topRectHeight - this.bottomRectHeight;
      // 判断悬浮窗是否超出内容区上下边界,根据结果将悬浮窗设置在边界位置
      if (this.positionY < Constants.PAGE_PADDING) {
        this.positionY = Constants.PAGE_PADDING;
      } else if (this.positionY > pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING) {
        this.positionY = pageHeight - Constants.FLOAT_WINDOW_HEIGHT - Constants.PAGE_PADDING;
      }
    })
    break;
  }

高性能知识点

不涉及

工程结构&模块类型

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

模块依赖

  1. 本实例依赖common模块中的日志工具类logger。
  2. 本示例依赖动态路由模块来实现页面的动态加载。

参考资料

@ohos.window (窗口)

显式动画 (animateTo)

@ohos.curves (插值计算)
为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1602460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】深度解析--拷贝构造函数(从0开始,详解浅拷贝到深拷贝,小白一看就懂!!!)

目录 一、前言 二、拷贝构造函数 &#x1f34e;概念解析 &#x1f95d;特性解析 &#x1f4a6;为什么拷贝构造函数使用传值方式会引发无穷递归调用&#xff1f; &#x1f4a6;为什么拷贝构造函数的形参中要加入 const 修饰 &#x1f4a6;若未显式定义&#xff0c;编译器会生…

c语言,单链表的实现----------有全代码!!!!

1.单链表的定义和结构 单链表是一种链式的数据结构&#xff0c;它用一组不连续的储存单元存反线性表中的数据元素。链表中的数据是以节点的形式来表示的&#xff0c;节点和节点之间相互连接 一般来说节点有两部分组成 1.数据域 &#xff1a;数据域用来存储各种类型的数据&…

深入理解同步与异步编程及协程管理在Python中的应用

文章目录 1. 同步与异步函数的对比1.1 同步函数1.2 异步函数1.3 对比 2. 管理多个协程与异常处理2.1 并发执行多个协程2.2 错误处理2.3 任务取消 本文将探索Python中同步与异步编程的基本概念及其区别。还会详细介绍如何使用asyncio库来有效管理协程&#xff0c;包括任务的创建…

一文读懂uniapp中的tabBar底部导航

目录 1. 基本知识2. Demo 1. 基本知识 UniApp 中的 tabBar 是用来在应用程序底部显示可切换的选项卡的组件&#xff0c;通常用于实现底部导航栏 允许用户通过点击不同的选项卡来切换应用程序的不同页面或功能模块 其代码如下&#xff1a; "tabBar":{"color&q…

UOS系统-mips架构---Java环境安装

平时都是在windows系统上安装的java环境&#xff0c;今天需要在uos系统安装java1.8的环境&#xff0c;记录一下安装过程。 &#xff08;以下均在root权限下运行&#xff09; 一、查找java1.8 jdk版本 apt search openjdkopenjdk-8-jdk/未知,未知 1.8.0.212-2deepin mips64el O…

车载摄像头画质增强解决方案,赋能智能驾驶新时代

在智能化浪潮席卷汽车产业的今天&#xff0c;车载摄像头作为智能驾驶的“眼睛”&#xff0c;其画质清晰度直接关系到车辆感知环境的准确性和驾驶的安全性。然而&#xff0c;面对复杂多变的行车环境&#xff0c;如何确保车载摄像头在不同场景下都能呈现出高质量的图像&#xff0…

分布式调度平台xxl-job

1.xxl-job介绍 xxl-job 是一个轻量级分布式任务调度框架&#xff0c;支持动态添加、修改、删除定时任务&#xff0c;支持海量任务分片执行&#xff0c;支持任务执行日志在线查看和分页查询&#xff0c;同时支持任务失败告警和重试机制&#xff0c;支持分布式部署和高可用。xxl…

一文弄懂Seaborn绘制热力图

1. 引言 在本文中&#xff0c;我们将使用Seaborn库来以heatmap热力图的形式来表示数据。我们将重点介绍如何创建它&#xff0c;以及如何更改其颜色&#xff0c;调整对应字体大小等等。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. 什么是热力图&#xff1f; Heatma…

Python 数学应用(四)

原文&#xff1a;zh.annas-archive.org/md5/123a7612a4e578f6816d36f968cfec22 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十一章&#xff1a;其他主题 在本章中&#xff0c;我们将讨论一些在本书前几章中没有涉及的主题。这些主题大多涉及不同的计算方式以及优…

向量数据库与图数据库:理解它们的区别

作者&#xff1a;Elastic Platform Team 大数据管理不仅仅是尽可能存储更多的数据。它关乎能够识别有意义的见解、发现隐藏的模式&#xff0c;并做出明智的决策。这种对高级分析的追求一直是数据建模和存储解决方案创新的驱动力&#xff0c;远远超出了传统关系数据库。 这些创…

C代码编译过程与进程内存分布

C代码编译过程 在这篇文章中&#xff0c;我们将探讨C语言代码的编译流程以及进程在运行时的内存布局。编译过程通常包括几个关键步骤&#xff1a;预处理、编译、汇编和链接。 预处理阶段主要是处理源代码文件中的宏定义、头文件包含和条件编译指令。在此阶段&#xff0c;编译…

ping命令的使用

一、实验环境 同实验案例分析ARP解析过程环境。 二、需求描述 熟悉 ping 命令的用法并熱悉 ping 命令的各种参数 三、推荐步骤 分别 ping 一个存在的和不存在的IP地址&#xff0c;观察返回的信息分别测试 ping 命令的相关参数。 四、实验步骤 1.ping 一个存在的和不存在…

工业电脑在ESOP工作站行业应用

ESOP工作站行业应用 项目背景 E-SOP是实现作业指导书电子化&#xff0c;并统一管理和集中控制的一套管理信息平台。信迈科技的ESOP终端是一款体积小巧功能齐全的高性价比工业电脑&#xff0c;上层通过网络与MES系统连接&#xff0c;下层连接显示器展示作业指导书。ESOP控制终…

FPGA - ZYNQ 基于EMIO的PS和PL交互

前言&#xff1a; Xilinx ZYNQ系列的芯片&#xff0c;GPIO分为 MIO 、EMIO、AXI_GPIO三种方式。 MIO &#xff1a;固定管脚&#xff0c;属于PS端&#xff0c;也就是ARM端。 EMIO &#xff1a;通过PL扩展&#xff0c;使用时需要分配PL(FPGA)管脚&#xff0c;消耗PL端资源。…

redis-plus-plus的安装与使用

文章目录 一、安装第一步&#xff1a;安装hiredis第二步&#xff1a;安装redis-plus-plus第三步&#xff1a;将编译后的可执行文件移动到/usr/local对应目录第四步&#xff1a;更新动态库 二、使用第一步&#xff1a;编写示例代码第二步&#xff1a;编译运行 本文参考自 redis-…

Pytest测试用例中的mark用法(包含代码示例与使用场景详解)

在软件开发中&#xff0c;测试是确保代码质量和功能稳定性的重要环节。Python作为一门流行的编程语言&#xff0c;拥有丰富的测试工具和框架&#xff0c;其中pytest是其中之一。pytest提供了丰富的功能来简化测试用例的编写&#xff0c;其中的mark功能允许我们对测试用例进行标…

Pytest精通指南(16)利用skip、skipif跳过用例执行

文章目录 前言skip源码分析skip装饰方法skip装饰类skip装饰模块skipif源码分析skipif装饰方法skipif装饰类skipif装饰模块拓展-用例内部跳过执行 前言 skip: skip用于无条件地跳过测试用例&#xff0c;无论测试环境的状态或条件如何。通常用于那些在任何情况下都不应该执行的测…

idea使用plantuml插件报错(类图):Dot Executable: /opt/local/bin/dot

报错提示&#xff1a; 解决方式&#xff1a; 方式一: 直接设置Remote Rendering即可 &#xff08;使用服务器地址&#xff09; 无特殊要求可直接使用默认提供的服务地址&#xff0c;也可自行搭建服务替换地址。 自行搭建服务可参考&#xff1a; 在本地Windows 11 系统的桌面…

分布式调度器

xxl-job介绍 xxl-job 是一个轻量级分布式任务调度框架&#xff0c;支持动态添加、修改、删除定时任务&#xff0c;支持海量任务分片执行&#xff0c;支持任务执行日志在线查看和分页查询&#xff0c;同时支持任务失败告警和重试机制&#xff0c;支持分布式部署和高可用。xxl-j…

中文编程入门(Lua5.4.6中文版)第十三章 Lua 文件操作

在《Lua世界》的冒险旅途中&#xff0c;勇士们时常需要与神秘的文本卷轴打交道。为了更好地掌握这些知识宝藏&#xff0c;Lua I/O库提供了两种强大的探索模式&#xff1a;简单模式和完全模式&#xff0c;助你轻松应对各类文献挑战。 简单模式&#xff1a;初识卷轴 简单模式如…