代码拉取完成,页面将自动刷新
本示例使用position绝对定位实现应用内悬浮窗,并且通过animateTo结合curves动画曲线实现悬浮窗拖拽跟手和松手吸附边缘的弹性动画效果。
使用说明
按住悬浮窗可以拖拽,松开后悬浮窗自动靠左或靠右,如果悬浮窗超出内容区上下边界,自动吸附在边界位置。
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({ x: this.positionX, y: this.positionY })
.onTouch((event: TouchEvent) => {
this.onTouchEvent(event);
})
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));
}
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));
}
case TouchType.Down: {
this.offsetX = event.touches[0].x;
this.offsetY = event.touches[0].y;
break;
}
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;
}
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 // 视图层-悬浮窗首页
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。