HarmonyOS 6商城开发学习:下拉进入二楼——让首页“藏”起来的惊喜空间
熟悉我们购物比价应用的老朋友一定还记得,之前首页做得中规中矩:顶部搜索栏、轮播Banner、分类入口、限时抢购、推荐瀑布流……该有的都有了,但总觉得少了点“彩蛋”。用户每天打开应用,看到的都是一样的内容,久而久之审美疲劳。直到有一天产品经理提出一个想法:“能不能在首页加一个隐藏空间,用户下拉首页就能进入,里面放一些高佣商品、限时秒杀或者趣味互动?”
这个需求听起来很酷,但实现起来有几个难点:
-
首页本身就是一个可滚动的长列表,怎么区分“正常下拉刷新”和“下拉进入二楼”?
-
下拉过程中要给用户明确的视觉反馈,不能突然跳转。
-
二楼页面要有一整套独立的布局,不能和首页内容混在一起。
我们调研了华为官方文档的“首页下拉进入二楼”示例,结合自己的业务场景,最终实现了一套流畅的下拉二楼交互。这篇文章完整记录一下实现过程和踩坑经验。
功能设计
先说说预期效果。
用户在首页任意位置(不限于顶部)向下滑动,当滑动距离超过一定阈值时,页面顶部会缓缓展开一个“二楼”入口提示,继续下拉,二楼页面逐渐显露出来。松手后,如果下拉距离达到阈值,自动展开二楼;如果没达到,回弹到首页。整个过程伴随平滑的位移和透明度动画,就像在翻一本杂志的折页。
核心目标:
-
手势识别:区分正常滚动和下拉进入二楼的手势,互不干扰。
-
视觉反馈:下拉过程中,首页内容整体下移,露出二楼顶部,并显示“继续下拉进入二楼”的文字提示。
-
动画流畅:展开和回弹都有弹性动画,不能僵硬。
-
二楼独立:二楼是一个独立的页面容器,可以承载商品列表、活动页等。
核心API
|
API/组件 |
说明 |
|---|---|
|
|
监听触摸事件,捕获Down、Move、Up |
|
|
显式动画,控制二楼展开和回弹 |
|
|
触摸事件类型枚举 |
|
|
滚动控制器,用于获取当前滚动位置 |
|
|
层叠布局,用于将二楼放在首页下方 |
实现过程
手势拦截与距离计算
第一步,我们需要在首页最外层容器上监听触摸事件,但不能影响内部列表的正常滚动。关键在于:只有当我们检测到用户在当前滚动位置为0(即列表已经处于最顶部)且手指向下滑动时,才进入二楼模式。
// pages/MainPage.ets
@State isPulling: boolean = false;
@State pullDistance: number = 0;
private startY: number = 0;
private scroller: Scroller = new Scroller();
handleTouchDown(event: TouchEvent) {
this.startY = event.touches[0].y;
this.isPulling = true;
}
handleTouchMove(event: TouchEvent) {
if (!this.isPulling) return;
const currentY = event.touches[0].y;
const deltaY = currentY - this.startY;
// 只有向下滑动且列表在顶部时才处理
if (deltaY > 0 && this.scroller.currentOffset().yOffset <= 0) {
// 应用阻尼,让下拉手感更柔和
this.pullDistance = deltaY * 0.5;
// 根据下拉距离更新二楼露出的高度
this.updateSecondFloorPosition(this.pullDistance);
}
}
handleTouchUp() {
if (this.pullDistance > EXPAND_THRESHOLD) {
// 展开二楼
this.expandSecondFloor();
} else {
// 回弹
this.resetSecondFloor();
}
this.isPulling = false;
}
这里有一个关键点:deltaY * 0.5是阻尼系数。如果不加阻尼,下拉一点点二楼就露出来了,体验很生硬。加了0.5的系数后,用户需要下拉更多的距离才能看到二楼,手感更像“拉开抽屉”。
二楼容器与动画
二楼页面我们放在首页内容的下面,通过translate偏移来实现下拉效果。首页内容整体向下平移,二楼从上方滑入。
// 布局结构
Stack() {
// 二楼页面(位于底层)
SecondFloorView()
.translate({ y: this.secondFloorOffset - SCREEN_HEIGHT })
// 首页内容(位于上层)
Column() {
// ... 首页的各种组件
}
.translate({ y: Math.max(0, this.pullDistance) })
}
当用户下拉时,pullDistance增大,首页内容下移,二楼页面也随之向下移动(从负值向0靠近)。当pullDistance超过阈值时,调用animateTo将secondFloorOffset设置为SCREEN_HEIGHT,二楼完全占据屏幕。
expandSecondFloor() {
animateTo({ duration: 300, curve: Curve.EaseOut }, () => {
this.secondFloorOffset = SCREEN_HEIGHT;
this.pullDistance = SCREEN_HEIGHT;
});
}
resetSecondFloor() {
animateTo({ duration: 200, curve: Curve.EaseOut }, () => {
this.secondFloorOffset = 0;
this.pullDistance = 0;
});
}
二楼顶部的“拉手”提示
为了让用户知道下拉可以进入二楼,我们在二楼顶部设计了一个“拉手”区域,包含箭头图标和文字提示。当下拉距离较小时,只显示“继续下拉”;接近阈值时,提示变为“松手进入二楼”。
@Builder
buildPullHint() {
Column() {
Image($r('app.media.ic_arrow_up'))
.rotate({ angle: this.pullDistance > 30 ? 180 : 0 }) // 箭头翻转
.animation({ duration: 150 })
Text(this.pullDistance > EXPAND_THRESHOLD ? '松手进入二楼' : '继续下拉')
.fontSize(14)
.fontColor(Color.Gray)
}
.opacity(Math.min(1, this.pullDistance / 50))
.position({ y: -60 }) // 固定在二楼顶部
}
遇到的问题与解决方案
问题1:首页列表滚动和下拉手势冲突
一开始我们在首页的Scroll上直接监听onTouch,但发现当列表内部有可滚动区域(比如商品分类的横向滑动)时,手势会被吞掉。解决方案:将触摸监听放到最外层的Stack容器上,并通过hitTestBehavior设置透传,让内部组件仍然可以正常接收事件。
Stack()
.hitTestBehavior(HitTestMode.Transparent) // 透传触摸事件
.onTouch((event) => {
// 处理下拉手势
})
问题2:下拉回弹时首页内容抖动
当用户下拉后松手但未达到阈值,首页内容回弹时偶尔会出现抖动。原因是animateTo和Scroller的滚动动画产生了冲突。解决方案:在回弹动画开始时,先将Scroller锁定,禁止其滚动,动画结束后再解锁。
resetSecondFloor() {
this.scroller.disableScroll(); // 锁定滚动
animateTo({ duration: 200 }, () => {
this.secondFloorOffset = 0;
this.pullDistance = 0;
});
setTimeout(() => {
this.scroller.enableScroll(); // 解锁
}, 250);
}
问题3:二楼页面加载延迟
如果二楼页面内容较多(比如几十个商品),首次展开时会有明显的卡顿。解决方案:在首页加载时预先创建二楼页面实例,但设置visibility: Hidden,展开时改为Visible,这样内容已经提前渲染好了。
@State secondFloorVisible: boolean = false;
expandSecondFloor() {
this.secondFloorVisible = true; // 提前渲染
animateTo({ duration: 300 }, () => {
this.secondFloorOffset = SCREEN_HEIGHT;
});
}
总结
下拉进入二楼是一个小而美的交互创新,给用户带来“寻宝”般的惊喜感。核心实现要点总结如下:
|
要点 |
实现方式 |
|---|---|
|
手势识别 |
外层Stack监听onTouch,结合Scroller位置判断 |
|
阻尼效果 |
下拉距离乘以0.5系数,手感柔和 |
|
动画控制 |
animateTo控制二楼展开和回弹,曲线用EaseOut |
|
预渲染 |
二楼页面提前创建,展开时只做位移动画 |
|
冲突处理 |
hitTestMode透传 + 滚动锁定 |
改完之后,我们把这个功能上线做了A/B测试,结果令人惊喜:进入二楼页面的用户次日留存比首页直接跳转的活动页高出15%。用户喜欢这种“不经意间发现宝藏”的感觉。如果你也在做购物比价类应用,不妨试试给首页加一个“隐藏的二楼”。
更多推荐



所有评论(0)