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

使用说明
按住悬浮窗可以拖拽,松开后悬浮窗自动靠左或靠右,如果悬浮窗超出内容区上下边界,自动吸附在边界位置。
实现思路
- 悬浮窗组件使用Stack嵌套video布局,使用属性position绝对定位使组件悬浮。
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);
})
- 在悬浮窗组件的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));
}
- 使用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));
}
- 悬浮窗组件添加onTouchEvent回调,在手指按下时保存触摸点与悬浮窗左上角的偏移量offsetX和offsetY,用于移动时悬浮窗位置的计算。源码参考FloatWindowMainPage.ets
case TouchType.Down: {
this.offsetX = event.touches[0].x;
this.offsetY = event.touches[0].y;
break;
}
- 手指移动时,获取触摸点相对于应用窗口左上角的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;
}
- 手指抬起时,通过判断悬浮窗中心在水平方向位于窗口中心的左侧或右侧设置悬浮窗靠左或靠右,如果悬浮窗超出内容区上下边界,则将悬浮窗设置在边界位置,使用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、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.1024c.cn/archives/21296,转载请注明出处。
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.1024c.cn/archives/21296,转载请注明出处。
评论0