HarmonyOS 鸿蒙Next swiper的页面高度不同时的滑动效果
HarmonyOS 鸿蒙Next swiper的页面高度不同时的滑动效果
swiper的每个页面高度不同,随子组件内容自动变化,高度不可预知,在swiper左右滑动时,如何使下部的组件位置能平滑的移动,而不是突变。看了HarmonyOS NEXT应用开发案例里的Swiper高度可变化效果实现案例,实现的前提是它的页面高度是已知的,我的场景是,每个页面高度不可知的情况下如果平滑移动。
可以参考下gitee目前的现成项目: Swiper高度可变化效果实现
MainPage:
// swiper的第五个页面数据
private dataPageFive: GridItemInfo[] = [
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") },
];
// swiper第五个page
this.GridBuilderFunction(this.dataPageFive, Const.GRID_THIRD_HEIGHT, Const.GRID_THIRD_TEMPLATE)
// 如果页面是swiper的第一个page的话,上方有其他组件,swiper需要降低高度
if (page === this.dataPageOne) {
Column()
.margin({ top: this.swiperDistance })
.height($r('app.float.swipersmoothvariation_blank_height'))
.width($r('app.float.swipersmoothvariation_scroll_width'))
} else if (page === this.dataPageFive){
Column()
.margin(height === Const.GRID_THIRD_HEIGHT ?
{ top: Const.SWIPER_OFFSET + this.swiperDistance - Const.GRID_SINGLE_HEIGHT} :
{ top: Const.SWIPER_OFFSET + this.swiperDistance})
.height($r('app.float.swipersmoothvariation_blank_height'))
.width($r('app.float.swipersmoothvariation_scroll_width'))
} else {
Column()
.margin(height === Const.GRID_SINGLE_HEIGHT ?
{ top: Const.SWIPER_OFFSET + Const.GRID_SINGLE_HEIGHT + this.swiperDistance } :
{ top: Const.SWIPER_OFFSET + this.swiperDistance })
.height($r('app.float.swipersmoothvariation_blank_height'))
.width($r('app.float.swipersmoothvariation_scroll_width'))
}
if (index === 0 && extraInfo.currentOffset < 0) {
this.swiperDistance = extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE;
} else if (index === 1 && extraInfo.currentOffset > 0) {
this.swiperDistance = extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE - Const.SMALL_FONT_SIZE;
} else if (index === 2 && extraInfo.currentOffset < 0) {
this.swiperDistance = extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE - Const.SMALL_FONT_SIZE;
} else if (index === 3 && extraInfo.currentOffset > 0) {
this.swiperDistance = extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE - Const.SMALL_FONT_SIZE;
} else if (index === 4 && extraInfo.currentOffset < 0) {
this.swiperDistance = extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.GRID_SINGLE_HEIGHT - Const.SMALL_FONT_SIZE + Const.GRID_THIRD_HEIGHT;
}
if (targetIndex === 0) {
this.swiperDistance = 0;
} else if (targetIndex === 1 || targetIndex === 2 || targetIndex === 3) {
this.swiperDistance = -Const.SMALL_FONT_SIZE;
} else if (targetIndex === 4){
this.swiperDistance = -Const.SMALL_FONT_SIZE - Const.GRID_SINGLE_HEIGHT + Const.GRID_DOUBLE_HEIGHT;
}
// 第五页三行
static readonly GRID_THIRD_HEIGHT: number = 195;
static readonly GRID_THIRD_TEMPLATE: string = '1fr 1fr 1fr';
CommonConstants:
/**
* 代表自定义类型数据的接口。
* @interface
* @property {Resource} name - 名字。
* @property {Resource} image - 图片。
* @property {Resource} prompt - 角标。
*/
interface GridItemInfo {
name: Resource
image: Resource
prompt?: Resource
};
@CustomDialog
struct CustomDialogExample {
controller: CustomDialogController;
build() {
Column() {
Text($r('app.string.swipersmoothvariation_message_custom_dialog'))
}
.justifyContent(FlexAlign.Start)
}
}
/**
* 自定义组件
* 功能:自定义一个图标+文字组合
*/
@Component
struct ViewItem {
item: GridItemInfo = {
name: $r('app.string.swipersmoothvariation_name_pageone'),
image: "app.media.swipersmoothvariation_taxi",
prompt: $r('app.string.swipersmoothvariation_first_prompt')
};
build() {
Column() {
Stack() {
Image(this.item.image)
.height($r('app.float.swipersmoothvariation_app_side_length'))
.width($r('app.float.swipersmoothvariation_app_side_length'))
Text(this.item.prompt)
.fontColor($r('app.color.swipersmoothvariation_color_white'))
.fontSize($r('app.float.swipersmoothvariation_badge_font_size'))
.backgroundColor($r('app.color.swipersmoothvariation_badge_color'))
.borderRadius({
topLeft: $r('app.float.swipersmoothvariation_text_border_radius'),
topRight: $r('app.float.swipersmoothvariation_text_border_radius'),
bottomRight: $r('app.float.swipersmoothvariation_text_border_radius')
})
.textAlign(TextAlign.Center)
.width($r('app.integer.swipersmoothvariation_item_width'))
.height($r('app.integer.swipersmoothvariation_item_height'))
.margin({
top: $r('app.integer.swipersmoothvariation_margin_top'),
left: $r('app.integer.swipersmoothvariation_margin_left')
})
.visibility(this.item.prompt !== undefined ? Visibility.Visible : Visibility.None)
}
.width(Const.FULL_PERCENT)
Text(this.item.name)
.margin({ top: $r('app.float.swipersmoothvariation_item_text_offset') })
.fontSize($r('app.float.swipersmoothvariation_small_font_size'))
}
}
}
/**
* 页面高度随着swiper高度平滑变化
* 滑动swiper,页面高度随着swiper高度平滑变化
* 效果:当不同swiper页面的高度发生变化时,下方页面的高度也会随着一起平滑的变化
*/
@Entry
@Component
export struct SwiperSmoothVariation {
// scroller控制器初始化
private gridScroller: Scroller = new Scroller();
private dialog: CustomDialogController = new CustomDialogController({
builder: CustomDialogExample()
});
// swiper的第一个页面数据
private dataPageOne: GridItemInfo[] = [
//第一页数据
];
// swiper的第二个页面数据
private dataPageTwo: GridItemInfo[] = [
//第二页数据
];
// swiper的第三个页面数据
private dataPageThree: GridItemInfo[] = [
//第三页数据
];
// swiper的第四个页面数据
private dataPageFour: GridItemInfo[] = [
{ name: $r('app.string.swipersmoothvariation_name_pagefour'), image: $r("app.media.swipersmoothvariation_taxi") }
];
private dataPageFive: GridItemInfo[] = [
//第五页数据
];
// 下方页面对应swiper上下位置的变化
@State swiperDistance: number = 0;
@State targetIndex: number = 0;
// 自定义构建函数,生成每一个swiper页面的函数
@Builder
GridBuilderFunction(page: GridItemInfo[], height: number, template: string) {
Column() {
Grid(this.gridScroller) {
ForEach(page, (item: GridItemInfo) => {
GridItem() {
ViewItem({ item: item })
.onClick(() => {
this.dialog.open();
})
}
.width(Const.FULL_PERCENT)
.height(height)
.width($r('app.float.swipersmoothvariation_scroll_width'))
})
}
.height(height)
.width($r('app.string.swipersmoothvariation_width_full'))
.borderRadius($r('app.float.swipersmoothvariation_border_radius'))
.edgeEffect(EdgeEffect.None)
.columnsTemplate(Const.COLUMN_TEMPLATE)
.rowsTemplate(template)
// 如果页面是swiper的第一个page的话,上方有其他组件,swiper需要降低高度
if (page === this.dataPageOne) {
Column()
.margin({ top: this.swiperDistance })
.height($r('app.float.swipersmoothvariation_blank_height'))
.width($r('app.float.swipersmoothvariation_scroll_width'))
} else {
Column()
.margin(height === Const.GRID_SINGLE_HEIGHT ?
{ top: Const.SWIPER_OFFSET + Const.GRID_SINGLE_HEIGHT + this.swiperDistance } :
{ top: Const.SWIPER_OFFSET + this.swiperDistance })
.height($r('app.float.swipersmoothvariation_blank_height'))
.width($r('app.float.swipersmoothvariation_scroll_width'))
}
}
.margin(page === this.dataPageOne ? { top: $r('app.float.swipersmoothvariation_swiper_offset') } : 0)
}
// 创建一个stack组件,用来显示swiper和下方页面(下方column),创建swiper组件用来显示滑动效果
// 当swiper滑动时,下方页面(column)高度也会变化
build() {
Stack() {
Swiper() {
Column() {
Stack() {
Text('功能栏')
.textAlign(TextAlign.Center)
.margin({
top: $r('app.integer.swipersmoothvariation_margin_small'),
left: $r('app.integer.swipersmoothvariation_default_padding')
})
// swiper第一个page
this.GridBuilderFunction(this.dataPageOne, Const.GRID_DOUBLE_HEIGHT, Const.GRID_TEMPLATE)
}
.alignContent(Alignment.TopStart)
}
// swiper第二个page
this.GridBuilderFunction(this.dataPageTwo, Const.GRID_DOUBLE_HEIGHT, Const.GRID_TEMPLATE)
// swiper第四个page
this.GridBuilderFunction(this.dataPageFour, Const.GRID_SINGLE_HEIGHT, Const.GRID_SINGLE_TEMPLATE)
// swiper第三个page
// this.GridBuilderFunction(this.dataPageThree, Const.GRID_DOUBLE_HEIGHT, Const.GRID_TEMPLATE)
}
.width($r('app.string.swipersmoothvariation_width_full'))
.backgroundColor($r('app.color.swipersmoothvariation_color_white'))
.borderRadius($r('app.integer.swipersmoothvariation_border_radius'))
.margin({ top: $r('app.float.swipersmoothvariation_margin_fifteen') })
.effectMode(EdgeEffect.None)
.loop(false)
// 知识点: Swiper组件绑定onGestureSwipe事件,在页面跟手滑动过程中,逐帧触发该回调
// 性能知识点: onGestureSwipe属于频繁回调,不建议在onGestureSwipe做耗时和冗余操作
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
animateTo({
duration: Const.DURATION_SWIPER,
curve: Curve.EaseOut,
playMode: PlayMode.Normal,
onFinish: () => {
console.info('play end');
}
}, () => { // 通过左右滑动的距离来计算对应的上下位置的变化
if (index === 0) {
this.swiperDistance =
extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE;
} else if (index === 1) {
this.swiperDistance =
extraInfo.currentOffset / Const.SCROLL_WIDTH * 50 - 13;
} else if (index === 2) {
this.swiperDistance =
extraInfo.currentOffset / Const.SCROLL_WIDTH * 50 -78;
} else if (index === 3) {
this.swiperDistance =
-extraInfo.currentOffset / Const.SCROLL_WIDTH * Const.SMALL_FONT_SIZE - Const.SMALL_FONT_SIZE * 4;
}
})
})
// 平滑变化的动画效果
.onAnimationStart((_: number, targetIndex: number) => {
animateTo({
duration: Const.DURATION_DOWN_PAGE,
curve: Curve.EaseOut,
playMode: PlayMode.Normal,
onFinish: () => {
console.info('play end');
}
}, () => {
if (targetIndex === 0) {
this.swiperDistance = 0;
} else if (targetIndex === 1 || targetIndex === 3) {
this.swiperDistance = -Const.SMALL_FONT_SIZE;
} else {
this.swiperDistance = -Const.SMALL_FONT_SIZE - Const.GRID_SINGLE_HEIGHT;
}
})
})
// swiper指示器
.indicator(new DotIndicator()
.selectedItemWidth($r('app.float.swipersmoothvariation_select_item_width'))
.selectedItemHeight($r('app.float.swipersmoothvariation_select_item_height'))
.itemWidth($r('app.float.swipersmoothvariation_default_item_width'))
.itemHeight($r('app.float.swipersmoothvariation_default_item_height'))
.selectedColor($r('app.color.swipersmoothvariation_swiper_selected_color'))
.color($r('app.color.swipersmoothvariation_swiper_unselected_color')))
// swiper下方的页面
Image($r("app.media.swipersmoothvariation_test"))
.height($r('app.integer.swipersmoothvariation_height_1'))
.borderRadius($r('app.integer.swipersmoothvariation_border_radius'))
.width($r('app.string.swipersmoothvariation_width_full'))
.offset({ y: this.swiperDistance })
.margin({ top: $r('app.float.swipersmoothvariation_colum_offset_one') })
Text('高度:' + this.swiperDistance)
}
.backgroundColor($r('app.color.swipersmoothvariation_stack_color'))
.padding($r('app.integer.swipersmoothvariation_default_padding'))
.alignContent(Alignment.TopStart)
}
}
更多关于HarmonyOS 鸿蒙Next swiper的页面高度不同时的滑动效果的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
姓名
张三
职位
软件工程师
兴趣爱好
- 阅读
- 篮球
- 电影
希望HarmonyOS能继续优化系统稳定性,减少崩溃和重启的情况。
每个页面高度不可知的情况下如果平滑移动。
可以采用动态布局和过渡效果来解决问题。
动态计算高度并传递给下部组件,以 ArkUI 为例
首先,在Swiper的每个页面组件中,需要能够获取自身的高度。可以通过@Size装饰器来实现。假设Swiper的每个页面是一个自定义组件SwiperPage
<MyLowerComponent height={this.swiperPagesHeight[this.currentIndex]} />
在这里,MyComponent1和MyComponent2等SwiperPage中的组件在自身大小发生变化(高度变化)时,通过onSize事件将高度信息传递给MySwiper组件。MySwiper组件则维护一个数组swiperPagesHeight来存储每个SwiperPage的高度,并将当前SwiperPage的高度传递给MyLowerComponent。
另一种方法是,添加过渡效果,为了使下部组件的位置变化更加平滑,可以在MyLowerComponent的样式中添加过渡效果。
@Component
struct MyLowerComponent {
@Prop height: number;
build() {
return (
<Divider
style={{
height: this.height + 'px',
transition: 'height 0.3s ease-in-out'
}}
/>
);
}
}
上述代码中,当height属性发生变化时,Divider的高度会在 0.3 秒内以缓入缓出的方式进行过渡,从而实现平滑移动的效果。
啊这~~不是ArkUI吧,
![图片](https://example.com/image.png)
在HarmonyOS(鸿蒙)系统中,当使用Next swiper组件且页面高度不同时,滑动效果的处理主要依赖于swiper组件自身的布局管理和动画特性。鸿蒙系统为swiper组件提供了灵活的布局支持,可以适应不同高度的页面。
默认情况下,swiper组件会按照子页面的实际高度进行布局,滑动时会根据当前页面的高度进行平滑过渡。如果页面高度不一致,swiper会自动调整滑动动画,确保过渡效果自然流畅。
为了优化滑动体验,可以考虑以下几点:
- 确保每个swiper页面的布局合理,避免页面内元素过多导致性能问题。
- 利用鸿蒙的布局容器(如Stack、Column等)来精细控制页面布局,确保页面高度符合预期。
- 如果需要自定义滑动动画,可以通过设置swiper的动画属性(如滚动速度、滚动惯性等)来调整。
然而,鸿蒙系统本身已经对swiper组件的滑动效果进行了优化,以适应不同高度的页面。因此,在大多数情况下,开发者无需进行额外的配置即可获得良好的滑动体验。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html