场景描述
在一些应用的首页或者详情页上,需要原生组件与网页进行一些嵌套或者展开收起的逻辑。
场景一:在滑动场景中原生组件与web页面嵌套,需要先让原生组件的高度变化,等原生组件到底后web页面高度随之变化。
场景二:嵌套在列表的原生组件中的web页面,点击按钮可以展开或者收起。
方案描述
将web组件放置在List或者Scroll组件中,通过web的嵌套滚动属性nestedScroll和Scroll的onScrollFrameBegin属性实现场景一的场景效果。
封装一个web组件,在web的onpageEnd回调中拿到网页高度,当点击展开后将web的高度设置为拿到的高度即可实现场景二的效果。
场景一:在应用详情页面,上半部分展示简述,下方web页面展示详情内容
方案
在父组件Scroll里放置一个原生Image组件和一个web组件,给Image组件设置最大和最小高度。通过在Scroll组件每帧开始滚动时触发的回调onScrollFrameBegin中监听y轴高度,并通过一个变量传递到Image组件中,从而实现Image组件的高度动态变化。下半部分web组件使用控制嵌套滚动的方法nestedScroll和禁止滚动的方法setScrollable来实现一个吸顶的效果。
核心代码
在Scroll组件里设置Image和web组件,并将web组件nestedScroll属性的枚举值设置为变量。注意:Image组件最小高度为100vp,所以需要设置web的高度为calc('100%-100vp'),这样才能确保web页面完全展示出来。
import { webview } from '@kit.ArkWeb' import web_webview from '@ohos.web.webview' @Entry @Component struct Index { controller: web_webview.WebviewController = new web_webview.WebviewController() @State mOffset: number = 0 @State NestedScrollModeF: NestedScrollMode = NestedScrollMode.PARENT_FIRST @State NestedScrollModeB: NestedScrollMode = NestedScrollMode.SELF_FIRST private scroller: Scroller = new Scroller() build() { Scroll(this.scroller) { Column() { Image($rawfile('iimg.png')) .width('100%') .height(300 - this.mOffset) Web({ src: 'https://developer.huawei.com/consumer/cn/?ha_linker=eyJ0cyI6MTcwODEyOTY4MDk5MywiaWQiOiI5NGE4Y2U3YzZkNWFjMzI1M2VlOWRkNjBhMWNhYjMwZCJ9', controller: this.controller }) .nestedScroll({ scrollForward: this.NestedScrollModeF, scrollBackward: this.NestedScrollModeB }) .height('calc(100% - 100vp)') }.height('100%') } .edgeEffect(EdgeEffect.Spring) .backgroundColor('#DCDCDC') .scrollBar(BarState.On) .height('100%') .width('100%') .onScrollFrameBegin((offset: number) => { this.mOffset = Math.min(this.mOffset + offset, 200) this.mOffset = Math.max(this.mOffset, -150) console.log('sdasd ' + this.mOffset) if (this.mOffset < 200) { this.NestedScrollModeF = NestedScrollMode.PARENT_FIRST this.NestedScrollModeB = NestedScrollMode.PARENT_FIRST this.controller.setScrollable(false) } else if (this.mOffset = 200) { this.NestedScrollModeF = NestedScrollMode.SELF_FIRST this.NestedScrollModeB = NestedScrollMode.SELF_FIRST this.controller.setScrollable(true) } return { offsetRemain: 0 } }) } }
在onScrollFrameBegin回调里设置Image组件的最大高度和最小高度。
this.mOffset = Math.min(this.mOffset + offset, 200) this.mOffset = Math.max(this.mOffset, -150)
根据Image组件的动态高度设置scrollForward和scrollBackward的值来实现web组件的吸顶效果。
if (this.mOffset < 200) { this.NestedScrollModeF = NestedScrollMode.PARENT_FIRST this.NestedScrollModeB = NestedScrollMode.PARENT_FIRST this.controller.setScrollable(false) } else if (this.mOffset = 200) { this.NestedScrollModeF = NestedScrollMode.SELF_FIRST this.NestedScrollModeB = NestedScrollMode.SELF_FIRST this.controller.setScrollable(true) }
场景二:在新闻类应用的资讯场景中,会有可以展开收起的List类型新闻页面
方案
封装一个web组件,给这个web组件设置一个最小高度,所有的List列表都是以这个高度展示,点击展开后即可浏览全部内容。
在以上dom树的示例中,可以看出来父组件List中有两个listItem子组件和两个封装的web组件,封装的类里面是有一个Stack父组件以及web和column两个子组件,分别加载web页面和展开更多的文本。给web页面一个最小高度,这样就可实现以上List列表的效果,当用户想查看详情的时候,就可以点击展开更多,通过这个点击事件将web加载时拿到的页面高度在变量里取出来,赋值到web组件的高度上,这样就可以实现点击展开详情页的效果,滑动到最下面后再点击收起把web的高度恢复到最小值即可
核心代码
封装一个web组件,在这个page页内写上点击展开或收起的逻辑。
import web_webview from '@ohos.web.webview'; @Preview @Component export default struct WebMoreComponent { private webSrc: string | Resource = "" wController: web_webview.WebviewController = new web_webview.WebviewController(); @State webShowMore: boolean = false @State webHeight: number = 166 @State webHeight1: number = 166 bottomPadding = 33 @State minShowHeight: number = 160 refreshCurrent = 0 listScroller: Scroller = new Scroller() build() { if (this.webSrc != null) { Stack({ alignContent: Alignment.Bottom }) { Web({ src: this.webSrc, controller: this.wController, renderMode: RenderMode.SYNC_RENDER }) .width('100%') .backgroundColor(Color.White) .layoutWeight(1) .fileAccess(true) .height(this.webHeight) .javaScriptAccess(true) .domStorageAccess(true) .overviewModeAccess(true) .onlineImageAccess(true) .nestedScroll({ scrollForward: NestedScrollMode.SELF_FIRST, scrollBackward: NestedScrollMode.SELF_FIRST }) .verticalScrollBarAccess(false) .id("webTest") .onPageEnd(() => { this.webHeight1 = this.wController.getPageHeight() if (this.wController.getPageHeight() == 0 && this.refreshCurrent < 6) { this.refreshCurrent++ this.wController.refresh() } }) Column() { Blank().layoutWeight(1) .visibility(this.webShowMore ? Visibility.None : Visibility.Visible) Column() { Text().height(33).width('100%') .linearGradient({ direction: GradientDirection.Top, colors: [[0x00ffffff, 0.0], [0x0fffffff, 1]] }).visibility(this.webShowMore ? Visibility.None : Visibility.Visible) Row() { Text(this.webShowMore ? '收起' : '展开更多') .fontColor('#E91839') .fontSize(13) Image('') .width(9) .height(9) .margin({ left: 3 }) .rotate({ angle: this.webShowMore ? 180 : 0 }) } .height(33) .width('100%') .backgroundColor(Color.White) .padding({ bottom: 5.5 }) .justifyContent(FlexAlign.Center) .onClick(() => { this.webShowMore = !this.webShowMore this.webHeight = this.webHeight1 if (!this.webShowMore) { } }) }.visibility(((this.webHeight - this.minShowHeight) < 5) ? Visibility.None : Visibility.Visible) } }.height(this.webShowMore ? this.webHeight + this.bottomPadding : this.minShowHeight) .clip(true) } } }
当点击展开后,在web的页面加载完成后的回调onPageEnd中通过获取H5页面高度的方法getPageHeight来拿到整个H5页面高度,然后赋值给到变量webHeight1,当点击展开的时候,将webHeight1的值给到web的高度上,即可实现展开H5页面的效果。
.onPageEnd(() => { this.webHeight1 = this.wController.getPageHeight() if (this.wController.getPageHeight() == 0 && this.refreshCurrent < 6) { this.refreshCurrent++ this.wController.refresh() } })
Row() {
Text(this.webShowMore ? '收起' : '展开更多')
.fontColor('#E91839')
.fontSize(13)
Image('')
.width(9)
.height(9)
.margin({ left: 3 })
.rotate({ angle: this.webShowMore ? 180 : 0 })
}
.height(33)
.width('100%')
.backgroundColor(Color.White)
.padding({ bottom: 5.5 })
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.webShowMore = !this.webShowMore
this.webHeight=this.webHeight1
if (!this.webShowMore) {
}
})
新建一个page,设置父组件为List,将封装的web组件添加进去。
import web_webview from '@ohos.web.webview' import WebMoreComponent from './WebMoreComponent' @Entry @Component struct Index { private listScroller: Scroller = new Scroller(); build() { Column() { List({ scroller: this.listScroller }) { ListItem() { Image($rawfile('iimg.png')) .width('100%') .height(200) } WebMoreComponent({ webSrc: 'https://developer.huawei.com/consumer/cn/?ha_linker=eyJ0cyI6MTcwODEyOTY4MDk5MywiaWQiOiI5NGE4Y2U3YzZkNWFjMzI1M2VlOWRkNjBhMWNhYjMwZCJ9' }) WebMoreComponent({ webSrc: 'https://developer.huawei.com/consumer/cn/training/' }) ListItem() { Text('更多') .backgroundColor('#1890ff') .width('100%') .textAlign(TextAlign.Center) .height('30%') } } .scrollBar(BarState.Off) .height('100%') } } }
常见问题
1.场景一中为什么要使用web的禁止滚动的方法setScrollable?
因为web组件嵌套在父组件Scroll中并且向上滑动时,最开始并不需要web页面滑动,只需要手势作用的量传递到Image组件上,使其高度减少,当达到最小高度后就可以滚动了。
2.场景二中为什么要设置两个高度值变量?
因为一个高度值是设置的web页面最小高度值,另外一个是web页面具体高度,拿到这个值后在点击展开的点击事件中改变页面高度时机正好。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。