欢迎光临
我们一直在努力

HarmonyOS ArkWeb 系列之嵌套滚动:Web 组件与外层容器滚动联动完全指南

文章目录

    • 大白话讲问题
    • 方案一:nestedScroll 属性(简单版)
    • 方案二:onScrollFrameBegin 精细控制(进阶版)
    • 两种方案对比

把 Web 组件放在可滚动的外层容器里,用户滑动时谁先滚动、谁后滚动?默认行为往往让用户困惑。本文介绍两种嵌套滚动方案。

大白话讲问题

外层 Scroll → Web 组件(内容长)→ Native 卡片列表。用户向下滑:先滚 Web 内容?还是先滚外层?Web 滚完了才轮到外层?这就是嵌套滚动要解决的问题。

方案一:nestedScroll 属性(简单版)

import { webview } from '@kit.ArkWeb';

@Entry
@ComponentV2
struct NestedScrollSimple {
private scrollerForScroll: Scroller = new Scroller();
controller: webview.WebviewController = new webview.WebviewController();
@Local arr: Array<number> = [];

aboutToAppear(): void {
for (let i = 0; i < 10; i++) {
this.arr.push(i);
}
}

build() {
Scroll(this.scrollerForScroll) {
Column() {
Web({ src: $rawfile('scroll.html'), controller: this.controller })
.nestedScroll({
scrollUp: NestedScrollMode.PARENT_FIRST, // 向上:外层优先
scrollDown: NestedScrollMode.SELF_FIRST, // 向下:Web 自身优先
})
.height('100%')

Repeat<number>(this.arr)
.each((item: RepeatItem<number>) => {
Text('Native 卡片')
.width('100%')
.height('40%')
.backgroundColor(0x330000FF)
.fontSize(16)
.textAlign(TextAlign.Center)
})
}
}
}
}

NestedScrollMode 枚举值说明:

枚举值含义
SELF_FIRST 子组件优先,到边界后外层滚
PARENT_FIRST 外层优先,外层到底后子组件滚
SELF_ONLY 只有子组件滚,外层不联动
PARALLEL 子组件和外层同时滚动

方案二:onScrollFrameBegin 精细控制(进阶版)

import { webview } from '@kit.ArkWeb';

@Entry
@ComponentV2
struct NestedScrollAdvanced {
private scroller: Scroller = new Scroller();
private listScroller: Scroller = new Scroller();
private webController: webview.WebviewController = new webview.WebviewController();
private isWebAtEnd: boolean = false;
private webHeight: number = 0;
@Local arr: Array<number> = [];

aboutToAppear(): void {
for (let i = 0; i < 10; i++) {
this.arr.push(i);
}
}

getWebHeight(): void {
try {
this.webController.runJavaScriptExt('window.innerHeight', (error, result) => {
if (error || !result) return;
if (result.getType() === webview.JsMessageType.NUMBER) {
this.webHeight = result.getNumber();
}
});
} catch (error) {
console.error('getWebHeight error:', error);
}
}

checkScrollBottom(): void {
this.isWebAtEnd = false;
try {
const offset = this.webController.getPageOffset();
const pageHeight = this.webController.getPageHeight();
if (offset.y + this.webHeight >= pageHeight) {
this.isWebAtEnd = true;
}
} catch (err) {
console.error(`checkScrollBottom failed! Code: ${err.code}, Message: ${err.message}`);
}
}

build() {
Scroll(this.scroller) {
Column() {
Web({ src: $rawfile('scroll.html'), controller: this.webController })
.height('100%')
.bypassVsyncCondition(WebBypassVsyncCondition.SCROLLBY_FROM_ZERO_OFFSET)
.onPageEnd(() => {
// 禁止 Web 响应 EVENT 类型滚动,由外层统一分发
this.webController.setScrollable(false, webview.ScrollType.EVENT);
this.getWebHeight();
})
.onGestureRecognizerJudgeBegin((event: BaseGestureEvent,
current: GestureRecognizer, others: Array<GestureRecognizer>) => {
// 拒绝 Web 内置 PAN 手势,让外层 Scroll 接管
if (current.isBuiltIn() &&
current.getType() === GestureControl.GestureType.PAN_GESTURE) {
return GestureJudgeResult.REJECT;
}
return GestureJudgeResult.CONTINUE;
})

List({ scroller: this.listScroller }) {
Repeat<number>(this.arr)
.each((item: RepeatItem<number>) => {
ListItem() {
Text('Native 卡片')
.width('100%')
.height('40%')
.backgroundColor(0x330000FF)
.fontSize(16)
.textAlign(TextAlign.Center)
}
})
}
.height('100%')
.maintainVisibleContentPosition(true)
.enableScrollInteraction(false) // List 不响应手势,由外层分发
}
}
.onScrollFrameBegin((offset: number, state: ScrollState) => {
this.checkScrollBottom();

if (offset > 0) {
// 向下滑
if (!this.isWebAtEnd) {
this.webController.scrollBy(0, offset);
return { offsetRemain: 0 };
} else if (this.scroller.isAtEnd()) {
this.listScroller.scrollBy(0, offset);
return { offsetRemain: 0 };
}
} else if (offset < 0) {
// 向上滑
if (this.listScroller.currentOffset().yOffset > 0) {
this.listScroller.scrollBy(0, offset);
return { offsetRemain: 0 };
} else if (this.scroller.currentOffset().yOffset <= 0) {
this.webController.scrollBy(0, offset);
return { offsetRemain: 0 };
}
}
return { offsetRemain: offset };
})
}
}

两种方案对比

nestedScroll 属性onScrollFrameBegin 回调
复杂度 低,几行配置 高,手动管理状态
灵活性 有限 极高,完全自定义
适用场景 简单优先级控制 多层嵌套、精细边界控制
赞(0)
未经允许不得转载:171主机测评 » HarmonyOS ArkWeb 系列之嵌套滚动:Web 组件与外层容器滚动联动完全指南
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址