实现效果和样式
如下图:按住电话悬浮框,随着手指的拖动会滚动,同时当松开手指时如果在屏幕左半边,则自动移动到左边;反之会自动移动停靠到右边。
构建电话悬浮框的代码:就是一个Column>>Image + Text,然后设置圆角和通过shadow方法设置阴影即可。
- @Component
- export default struct FloatingWindowComponent {
- private res: Resource = $r('app.media.ic_call_green');
- private tips?: Resource = $r('app.string.Tips_call');
- build() {
- Column() {
- Image(this.res)
- .objectFit(ImageFit.Contain)
- .width('40%')
- .height('40%')
- Text(this.tips)
- .fontSize(12)
- .fontColor($r('app.color.background_green'))
- .fontWeight(FontWeight.Regular)
- .fontFamily($r('app.string.Font_family_regular'))
- }
- .width(80)
- .height(80)
- .backgroundColor($r('app.color.white'))
- .borderRadius(16)
- .shadow({ radius: 15, color: $r('app.color.btn_border_color') })
- .justifyContent(FlexAlign.SpaceAround)
- }
- }
复制代码 相关概念
实现随着手指的移动而移动,就必要获取手指当前的(x,y)坐标值。在onTouch事件TouchEvent来获取对应的位置,TouchEvent对象提供了(windowX,windowY)、(displayX,displayY)、(screenX,screenY)(已废弃)三对属性。好比我们想获取手指按下时对应的坐标位置,可以用如下代码:
- if (event.type === TouchType.Down) {
- this.moveStartX = event.touches[0].windowX; // 按下时获取X坐标初始值
- this.moveStartY = event.touches[0].windowY; // 按下时获取Y坐标初始值
- }
复制代码 详细的代表的意思如下:
实现思路
- 获取屏幕的宽度和高度,通过display获取屏幕的宽和高,然后通过px2vp将px转换成vp
- aboutToAppear() {
- display.getAllDisplays((err, data) => {
- // 拿到屏幕宽高的一半,作为判断基准值
- this.displayHalfWidth = data[0].width / 2;
- this.displayHalfHeight = data[0].height / 2;
- // 将拿到的px转为vp
- this.displayHalfWidth = px2vp(this.displayHalfWidth);
- this.displayHalfHeight = px2vp(this.displayHalfHeight);
- })
- }
复制代码
- 利用@state方法修饰当前(x,y)的坐标位置,当二者的值发生变化时,会更新组件的位置,实现悬浮框跟着手指移动的效果。
- @State positionX: number = 50; // 组件位置X
- @State positionY: number = 500; // 组件位置Y
- /下面的系列属性,官方给的demo中都加了@State修饰,其实没必要
- moveStartX: number = 0; // X方向起始点
- moveStartY: number = 0; // Y方向起始点
- moveEndX: number = 0; // X方向终点
- moveEndY: number = 0; // Y方向终点
- moveSumLengthX: number = 0; // X方向移动距离总和
- moveSumLengthY: number = 0; // Y方向移动距离总和
- moveStartTime: number = 0; // 触摸开始时间
- moveEndTime: number = 0; // 触摸结束时间
复制代码
- 监听组件的onTouch方法,监听TouchType.Down,TouchType.Move, TouchType.Up事件,盘算手指移动的位置,同时更新positionX和positionY,由于两个变量通过@State修饰,会刷新页面,实现悬浮框跟随者手指移动的效果,核心代码如下:
- if (event.type === TouchType.Move) {
- //省略部分代码
- // 跟手过程,使用responsiveSpringMotion曲线
- animateTo({ curve: curves.responsiveSpringMotion() }, () => {
- // 减去半径,以使球的中心运动到手指位置
- this.positionX = event.touches[0].windowX - this.diameter / 2;
- this.positionY = event.touches[0].windowY - this.diameter / 2 - 120;
- Logger.info(TAG, `move end, animateTo x:${this.positionX}, y:${this.positionY}`);
- })
- }
复制代码
- 将positionX和positionY设置给组件的position(x,y)方法:
全部源码
- import { curves, display } from '@kit.ArkUI';
- import { TitleBar } from '../../../../common/TitleBar'
- import FloatingWindowComponent from './FloatingWindowComponent';
- import Logger from '../../../../util/Logger';
- const TAG = '[FloatingWindowPage]';
- @Entry
- @Component
- struct FloatingWindowSample {
- private diameter: number = 120; // 触摸点相对偏移量
- @State positionX: number = 50; // 组件位置X
- @State positionY: number = 500; // 组件位置Y
- @State displayHalfWidth: number = 0; // 屏幕一半的宽
- @State displayHalfHeight: number = 0; // 屏幕一半的高
- @State moveStartX: number = 0; // X方向起始点
- @State moveStartY: number = 0; // Y方向起始点
- @State moveEndX: number = 0; // X方向终点
- @State moveEndY: number = 0; // Y方向终点
- @State moveSumLengthX: number = 0; // X方向移动距离总和
- @State moveSumLengthY: number = 0; // Y方向移动距离总和
- @State moveStartTime: number = 0; // 触摸开始时间
- @State moveEndTime: number = 0; // 触摸结束时间
- aboutToAppear() {
- display.getAllDisplays((err, data) => {
- // 拿到屏幕宽高的一半,作为判断基准值
- this.displayHalfWidth = data[0].width / 2;
- this.displayHalfHeight = data[0].height / 2;
- // 将拿到的px转为vp
- Logger.info(TAG, `aboutToAppear getAllDisplays data 1 width:${this.displayHalfWidth}, height:${this.displayHalfHeight}`);
- this.displayHalfWidth = px2vp(this.displayHalfWidth);
- this.displayHalfHeight = px2vp(this.displayHalfHeight);
- Logger.info(TAG, `aboutToAppear getAllDisplays data 2 width:${this.displayHalfWidth}, height:${this.displayHalfHeight}`);
- })
- }
- build() {
- Row() {
- Column() {
- TitleBar({ title: $r('app.string.Floating_window') })
- .id('target')
- Row() {
- Row() {
- FloatingWindowComponent()
- }
- .id('floatingWindowComponent')
- .width(80)
- .height(80)
- .position({ x: this.positionX, y: this.positionY })
- .onTouch((event: TouchEvent) => {
- if (event.type === TouchType.Down) {
- this.moveStartX = event.touches[0].windowX; // 按下时获取X坐标初始值
- this.moveStartY = event.touches[0].windowY; // 按下时获取Y坐标初始值
- this.moveStartTime = Date.now(); // 按下时开始时间
- this.moveSumLengthX = 0; // 按下时初始化x方向移动距离
- this.moveSumLengthY = 0; // 按下时初始化y方向移动距离
- }
- if (event.type === TouchType.Move) {
- this.moveEndX = event.touches[0].windowX; // X方向移动的当前位置
- this.moveEndY = event.touches[0].windowY; // Y方向移动的当前位置
- this.moveSumLengthX += Math.abs(this.moveEndX - this.moveStartX); // 每一次移动计算相对于上一次X方向位置的距离
- this.moveSumLengthY += Math.abs(this.moveEndY - this.moveStartY); // 每一次移动计算相对于上一次Y方向位置的距离
- this.moveStartX = this.moveEndX;
- this.moveStartY = this.moveEndY;
- Logger.info(TAG, `move ing, moveSumLengthX:${this.moveSumLengthX}, moveSumLengthY:${this.moveSumLengthY}`);
- // 跟手过程,使用responsiveSpringMotion曲线
- animateTo({ curve: curves.responsiveSpringMotion() }, () => {
- // 减去半径,以使球的中心运动到手指位置
- this.positionX = event.touches[0].windowX - this.diameter / 2;
- this.positionY = event.touches[0].windowY - this.diameter / 2 - 120;
- Logger.info(TAG, `move end, animateTo x:${this.positionX}, y:${this.positionY}`);
- })
- } else if (event.type === TouchType.Up) {//手指抬起时自动靠边处理
- this.moveEndTime = Date.now();
- let moveDiffTime = this.moveEndTime - this.moveStartTime; // 最后一秒移动的距离
- // 距离
- let s = Math.sqrt((this.moveSumLengthX * this.moveSumLengthX) + (this.moveSumLengthY * this.moveSumLengthY));
- // 时间
- let t = moveDiffTime;
- // 速度
- let v = s / t;
- Logger.info(TAG, `moveEnd, moveSumLengthX:${this.moveSumLengthX}, moveSumLengthY:${this.moveSumLengthY}, moveDiffTime:${moveDiffTime}`);
- Logger.info(TAG, `moveEnd, s:${s}, t:${t}, v:${v}`);
- // 离手时,使用springMotion曲线,且将移动时速度赋值给离手时速度
- animateTo({ curve: curves.springMotion(), tempo: v }, () => {
- if (this.positionX >= this.displayHalfWidth) {
- // 如果划到右边,则定位至屏幕右边减去自身宽度80,再减去10留出间隙
- this.positionX = this.displayHalfWidth * 2 - 90;
- } else {
- this.positionX = 10;
- }
- if (this.positionY >= this.displayHalfHeight * 2 - 300) {
- this.positionY = this.displayHalfHeight * 2 - 300;
- } else if (this.positionY <= 0) {
- this.positionY = 10;
- }
- Logger.info(TAG, `touchUp, animateTo x:${this.displayHalfWidth}, y:100`);
- })
- }
- })
- }
- .width('100%')
- .height('92%')
- }
- .width('100%')
- .height('100%')
- .backgroundColor($r('app.color.background_shallow_grey'))
- }
- .width('100%')
- .height('100%')
- }
- }
复制代码 参考资料
源码传送门:
下载“语言-语言基础类库”,运行后进入:动画>专场动画>悬浮窗。即可看到运行效果。
HarmonyOS鸿蒙学习条记(5)@State作用说明和简朴案例
HarmonyOS鸿蒙学习条记(17)获取屏幕宽高等属性
触摸事件
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |