HarmonyOS 鸿蒙Next ArkUI如何实现类似百度地图这种分段式拖拽的功能

发布于 1周前 作者 eggper 来自 鸿蒙OS

HarmonyOS 鸿蒙Next ArkUI如何实现类似百度地图这种分段式拖拽的功能

这个效果并不是bindSheet 那种打开就遮挡了底部的tabbar栏的全屏模态的视图。百度这种拖拽到底部后,拖拽的上层视图并不会关闭,而是始终显示在父控件的底部,然后一往上拖拽就显示全屏,往下滑就回到了最底下并保持一定的可见区域,上层视图内也能嵌套listview这种可滚动的控件,底层视图也是可以交互的,哪位大佬知道如何实现!谢谢

_8634e774166abd3be81f08ddf6009915.gif


更多关于HarmonyOS 鸿蒙Next ArkUI如何实现类似百度地图这种分段式拖拽的功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

你要的是这种效果吗?

const windowHeight = px2vp(AppStorage.get<number>('windowHeight') || 2340);

[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
 [@State](/user/State) contentHeight:number = windowHeight;

 build() {
   Column(){
     Text('我是下层的文本')
       .fontSize(50)
       .bindSheet(true, this.sheetBuilder, {
         detents:[50, SheetSize.MEDIUM, SheetSize.LARGE],
         showClose:false,
         onDetentsDidChange:number=>{
           this.contentHeight = windowHeight - px2vp(number) - 50;
           console.log('windowHeight:', windowHeight, 'contentHeight:', this.contentHeight)
         }
       })
   }
   .justifyContent(FlexAlign.Center)
   .width('100%')
   .height(this.contentHeight)
 }

 [@Builder](/user/Builder)
 sheetBuilder(){
   Text('我是半模态页面')
 }
}

cke_1060.gif

更多关于HarmonyOS 鸿蒙Next ArkUI如何实现类似百度地图这种分段式拖拽的功能的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


import { hilog } from '[@kit](/user/kit).PerformanceAnalysisKit'
import { LengthMetrics } from '[@ohos](/user/ohos).arkui.node'

[@Entry](/user/Entry)
[@ComponentV2](/user/ComponentV2)
struct FloatPage {
private readonly contentMaxHeight: number = 540
private readonly contentMinHeight: number = 90
[@Local](/user/Local) floatMarginTop: number = this.contentMinHeight
[@Local](/user/Local) maskColor: ResourceColor = Color.Transparent
private scroller: Scroller = new Scroller()
private touchStartScrollY: number = 0
private touchStartMargin: number = 0

build() {
Stack({ alignContent: Alignment.Top }) {
Column() {
Text('我是地图 Top')
Blank()
Text('我是地图 Bottom')
}
.width('100%')
.height(this.contentMaxHeight)
.onClick(() => {
hilog.debug(0x000000, 'rainrain', 'content map click')
})

Column() {

}
.backgroundColor(this.maskColor)
.size({ width: '100%', height: '100%' })
.onTouchIntercept(() => {
return HitTestMode.Transparent
})

Scroll(this.scroller) {
Column() {
Column()
.width('100%')
.height('50%')
.backgroundColor(Color.Brown)
.onClick(() => {
hilog.debug(0x000000, 'rainrain', 'float child click')
})
Column()
.width('100%')
.height('50%')
.backgroundColor(Color.Green)
Column()
.width('100%')
.height('50%')
.backgroundColor(Color.Orange)
}
}
.width('100%')
.height('100%')
.margin({ top: this.floatMarginTop })
.backgroundColor(Color.Black)
.borderRadius({
topStart: LengthMetrics.vp(16),
topEnd: LengthMetrics.vp(16),
})
.enableScrollInteraction(false)
.gesture(PanGesture()
.onActionStart(() => {
this.touchStartMargin = this.floatMarginTop
this.touchStartScrollY = this.scroller.currentOffset().yOffset
})
.onActionUpdate((event) => {
let currentOffset = this.scroller.currentOffset().yOffset
if (currentOffset != 0) {
this.scroller.scrollTo({
xOffset: 0,
yOffset: this.touchStartScrollY - event.offsetY
})
return
}
if (this.touchStartMargin == this.contentMinHeight && event.offsetY < 0) {
this.scroller.scrollTo({
xOffset: 0,
yOffset: this.touchStartScrollY - event.offsetY
})
}
let newMargin = this.touchStartMargin + event.offsetY
newMargin = Math.max(newMargin, this.contentMinHeight)
newMargin = Math.min(newMargin, this.contentMaxHeight)
this.floatMarginTop = newMargin
})
.onActionEnd((event) => {
if (this.scroller.currentOffset().yOffset != 0) {
this.scroller.fling(event.velocityY)
return
}
let newMargin = this.contentMaxHeight
if (event.velocityY > 100) {
newMargin = this.contentMaxHeight
} else if (event.velocityY < -100) {
newMargin = this.contentMinHeight
} else if (this.floatMarginTop < this.contentMinHeight * 3) {
newMargin = this.contentMinHeight
} else {
newMargin = this.contentMaxHeight
}
let color: ResourceColor = Color.Transparent
if (newMargin == this.contentMinHeight) {
color = '#8F000000'
}
this.getUIContext().animateTo({ duration: 250 }, () => {
this.floatMarginTop = newMargin
this.maskColor = color
})
})
)
}.size({ width: '100%', height: '100%' })
.clip(true)
}
}

嵌套滑动没写出来,只能用Pan+Scroller来通过事件判断。 Scroll+Pan只响应scroll。通过 .enableScrollInteraction(false)干掉滑动。然后在Pan里面处理事件,内部滑动是Pan里通过scroller.scrollTo 来处理的。 外部滑动是通过 Scroll 的marginTop 不断变化来的。 半透明的背景是一直存在,需要处理Touch,让这个背景不响应任何触摸事件,让下面内部地图/内容区域可以点。

有劳,大概毛坯就是这样,接下来我再装修一下,感谢

手搓的吧,写个容器拖动事件时改变高度的回调就行了

在HarmonyOS鸿蒙系统中,使用ArkUI实现类似百度地图这种分段式拖拽的功能,可以通过以下步骤实现:

  1. 组件布局:利用ArkUI的布局系统(如Flexbox、Grid等)设计你的页面布局,确保有可拖拽的组件。

  2. 监听拖拽事件:为可拖拽的组件添加拖拽事件监听,如onPanDownonPanUpdateonPanCancelonPanEnd

  3. 处理拖拽逻辑

    • onPanDown中记录拖拽起始位置。
    • onPanUpdate中计算拖拽距离,并分段处理拖拽逻辑,如每达到某个距离触发一次动作或更新一次位置。
    • onPanCancelonPanEnd中处理拖拽取消或结束时的逻辑。
  4. 分段控制:通过判断拖拽距离与预设的分段距离的关系,控制拖拽过程中的不同响应或状态更新。

  5. 视觉反馈:根据拖拽的进度或分段,更新界面元素的视觉状态,如颜色变化、透明度变化等,以提供用户反馈。

  6. 边界控制:确保拖拽在合理的边界内,避免超出屏幕范围或造成界面混乱。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部