【HarmonyOS开发】ArkUI实现下拉革新/上拉加载

打印 上一主题 下一主题

主题 978|帖子 978|积分 2934


    列表下拉革新、上拉加载更多,不管在web时代还是鸿蒙应用都是一个非常常用的功能,基于ArkUI中TS扩展的声明式开发范式实现一个下拉革新,上拉加载。
  上拉加载、下拉革新

如果数据量过大,可以使用LazyForEach代替ForEach
高阶组件-上拉加载,下拉革新
https://gitee.com/bingtengaoyu/harmonyos-advanced-componen/tree/master/UpDownRefresh 
1、涉及的知识点



  • 列表容器(List)
  • 触摸事件(onTouch)
  • 位置设置(offset)
  • 显示动画(animateTo)
2、效果图


3、实现思绪

根据触摸事件onTouch()处置惩罚下拉和上拉,通过记录手指按下的y坐标和move移动的距离判断属于刷拉还是下滑,从而展示不同的内容。
4、关键代码

4.1 天生下拉革新/上拉加载DOM

  1.   @Builder UpDownRefreshBuilder(text: string, state: string) {
  2.     Row() {
  3.       Image($r('app.media.refreshing'))
  4.         .width(32)
  5.         .height(32)
  6.       Text(text).fontSize(16)
  7.     }
  8.     .justifyContent(FlexAlign.Center)
  9.     .position({
  10.       y: state === 'down' ? 20 : -20
  11.     })
  12.     .zIndex(999)
  13.     .width('94%')
  14.     .height('8%')
  15.   }
复制代码
4.2 onTouch处置惩罚事件

  1.   private currentOffsetY: number = 0;
  2.   private timer: number = 0;
  3.   @State refreshStatus: boolean = false;
  4.   @State upRefreshStatus: boolean = false;
  5.   aboutToDisappear() {
  6.     this.timer = null
  7.   }
  8.   putDownRefresh(event?: TouchEvent): void {
  9.     if (event === undefined) {
  10.       return;
  11.     }
  12.     switch (event.type) {
  13.       case TouchType.Down:
  14.         this.currentOffsetY = event.touches[0].y;
  15.         break;
  16.       case TouchType.Move:
  17.         if(this.scroller.currentOffset().yOffset < 50) {
  18.           this.refreshStatus = event.touches[0].y - this.currentOffsetY > 50;
  19.         }
  20.         this.upRefreshStatus = event.touches[0].y - this.currentOffsetY < -50;
  21.         break;
  22.       case TouchType.Cancel:
  23.         break;
  24.       case TouchType.Up:
  25.         // Only simulation effect, no data request
  26.         this.timer = setTimeout(() => {
  27.           if (this.upRefreshStatus) {
  28.             this.scroller.scrollTo({           // 调用scrollTo滚动到具体位置
  29.               xOffset: 0,                    // 竖直方向滚动,该值不起作用
  30.               yOffset: 680,    // 滚动到底部
  31.               animation: {                     // 滚动动画
  32.                 duration: 1500,
  33.                 curve: Curve.EaseOut
  34.               }
  35.             })
  36.           }
  37.           this.refreshStatus = false;
  38.           this.upRefreshStatus = false;
  39.         }, 1500);
  40.         break;
  41.     }
  42.   }
复制代码
5、完备代码

兵腾傲宇/harmonyos-healthy-live - Gitee.com
  1. import router from '@ohos.router'import curves from '@ohos.curves'import { BreakpointSystem, BreakPointType } from '../common/BreakpointSystem'import { FoodInfo, Category } from '../model/DataModels'import { getFoods, getFoodCategories, getSortedFoodData } from '../model/DataUtil'import { Records } from './components/DietRecord'import { PersonalCenter } from './PersonalCenter'interface FoodId {  foodId: FoodInfo;}@Componentstruct FoodListItem {  private foodItem?: FoodInfo  build() {    Navigator({ target: 'pages/FoodDetail' }) {      Row() {        Image(this.foodItem!.image!)          .objectFit(ImageFit.Contain)          .autoResize(false)          .height(40)          .width(40)          .backgroundColor('#FFf1f3f5')          .margin({ right: 16 })          .borderRadius(6)          .sharedTransition(this.foodItem!.letter, {            duration: 400,            curve: curves.cubicBezier(0.2, 0.2, 0.1, 1.0),            delay: 100          })        Text(this.foodItem?.name)          .fontSize(14)        Blank()        Text($r('app.string.calorie_with_kcal_unit', this.foodItem?.calories.toString()))          .fontSize(14)      }      .height(64)      .width('100%')    }    .params({ foodId: this.foodItem } as FoodId)    .margin({ right: 24, left: 32 })  }}@Componentstruct ListModeFoods {  private foodItems: Array<FoodInfo | string> = getSortedFoodData()  private currentOffsetY: number = 0;
  2.   private timer: number = 0;
  3.   @State refreshStatus: boolean = false;
  4.   @State upRefreshStatus: boolean = false;
  5.   aboutToDisappear() {
  6.     this.timer = null
  7.   }
  8.   putDownRefresh(event?: TouchEvent): void {
  9.     if (event === undefined) {
  10.       return;
  11.     }
  12.     switch (event.type) {
  13.       case TouchType.Down:
  14.         this.currentOffsetY = event.touches[0].y;
  15.         break;
  16.       case TouchType.Move:
  17.         if(this.scroller.currentOffset().yOffset < 50) {
  18.           this.refreshStatus = event.touches[0].y - this.currentOffsetY > 50;
  19.         }
  20.         this.upRefreshStatus = event.touches[0].y - this.currentOffsetY < -50;
  21.         break;
  22.       case TouchType.Cancel:
  23.         break;
  24.       case TouchType.Up:
  25.         // Only simulation effect, no data request
  26.         this.timer = setTimeout(() => {
  27.           if (this.upRefreshStatus) {
  28.             this.scroller.scrollTo({           // 调用scrollTo滚动到具体位置
  29.               xOffset: 0,                    // 竖直方向滚动,该值不起作用
  30.               yOffset: 680,    // 滚动到底部
  31.               animation: {                     // 滚动动画
  32.                 duration: 1500,
  33.                 curve: Curve.EaseOut
  34.               }
  35.             })
  36.           }
  37.           this.refreshStatus = false;
  38.           this.upRefreshStatus = false;
  39.         }, 1500);
  40.         break;
  41.     }
  42.   }  @Builder DownRefreshBuilder(text: string, state: string) {    Row() {      Image($r('app.media.refreshing'))        .width(32)        .height(32)      Text(text).fontSize(16)    }    .justifyContent(FlexAlign.Center)    .position({      y: state === 'down' ? 20 : -20    })    .zIndex(999)    .width('94%')    .height('8%')  }  private scroller: Scroller = new Scroller(); // 创建一个滚动控制器  build() {    Column() {      Text($r("app.string.title_food_list"))        .width('100%')        .height(56)        .padding({ left: 20 })        .backgroundColor('#FF1f3f5')        .fontSize(20)      Scroll(this.scroller) {        if(this.refreshStatus) {          this.DownRefreshBuilder('正在革新', 'down')        }        List() {          ForEach(this.foodItems, (item: FoodInfo) => {            ListItem() {              if (item.letter !== undefined) {                FoodListItem({ foodItem: item })              } else {                if (typeof (item) === 'string') {                  Text(item)                    .fontSize(14)                    .height(48)                    .margin({ left: 24 })                    .width('100%')                }              }            }          })          if(this.upRefreshStatus) {            ListItem(){              this.DownRefreshBuilder('正在加载', 'up')            }          }        }        .layoutWeight(1)      }      .scrollBar(BarState.Off)      .edgeEffect(EdgeEffect.Spring)      .width('100%')      .height('90%')      .onTouch((event?: TouchEvent) => {        this.putDownRefresh(event);      })    }  }}@Componentstruct FoodGridItem {  private foodItem?: FoodInfo  build() {    Column() {      Image(this.foodItem!.image!)        .objectFit(ImageFit.Contain)        .backgroundColor('#f1f3f5')        .width('100%')        .height(152)        .sharedTransition(this.foodItem!.letter, {          duration: 400,          curve: curves.cubicBezier(0.2, 0.2, 0.1, 1.0),          delay: 100        })      Row() {        Text(this.foodItem?.name)          .fontSize(14)        Blank()        Text($r('app.string.calorie_with_kcal_unit', this.foodItem?.calories.toString()))          .fontSize(14)          .fontColor(0x99000000)      }      .padding({ left: 12, right: 12 })      .width('100%')      .height(32)      .backgroundColor('#E5E5E5')    }    .height(184)    .clip(new Rect({ width: '100%', height: '100%', radius: 12 }))    .onClick(() => {      router.pushUrl({ url: 'pages/FoodDetail', params: { foodId: this.foodItem } })    })  }}@Componentstruct FoodGrid {  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'  private foodItems?: FoodInfo[]  build() {    Grid() {      ForEach(this.foodItems!, (item: FoodInfo) => {        GridItem() {          FoodGridItem({ foodItem: item })        }      })    }    .columnsTemplate(new BreakPointType({      sm: '1fr 1fr',      md: '1fr 1fr 1fr',      lg: '1fr 1fr 1fr 1fr'    }).getValue(this.currentBreakpoint) as string)    .columnsGap(8)    .rowsGap(8)    .padding({ left: 16, right: 16 })  }}@Componentstruct CategoryModeFoods {  @State currentTabIndex: number = 0  private foodItems: FoodInfo[] = getFoods()  private foodCategories: Category[] = getFoodCategories()  /* *   * 头部分类导航栏   *   * */  @Builder  tabBarItemBuilder(value: Resource, index: number) {    Text(value)      .fontColor(this.currentTabIndex === index ? Color.Blue : 'rgba(0,0,0,0.6)')      .fontSize(this.currentTabIndex === index ? 20 : 18)      .fontWeight(this.currentTabIndex === index ? FontWeight.Bold : FontWeight.Normal)      .margin({ top: 2 })      .height(56)  }  build() {    Tabs() {      TabContent() {        FoodGrid({ foodItems: this.foodItems })      }.tabBar(this.tabBarItemBuilder($r('app.string.category_all'), 0))      ForEach(this.foodCategories, (foodCategory: Category, index?: number) => {        TabContent() {          FoodGrid({ foodItems: this.foodItems.filter(item => (item.categoryId === foodCategory.id)) })        }.tabBar(this.tabBarItemBuilder(foodCategory.name!,          index! + 1))      })    }    .animationDuration(0)    .barWidth('80%')    .onChange((index) => {      this.currentTabIndex = index    })  }}@Componentstruct FoodsDisplay {  @State isCategoryMode: boolean = true  @State isMoreIconOnClick: boolean = false  @State isMoreIconOnHover: boolean = false  @State isMoreIconOnFocus: boolean = false  getMoreIconBgColor() {    if (this.isMoreIconOnClick) {      return $r('sys.color.ohos_id_color_click_effect')    } else if (this.isMoreIconOnHover) {      return $r('sys.color.ohos_id_color_hover')    } else {      return this.isCategoryMode ? Color.White : '#F1F3F5' || Color.Transparent    }  }  build() {    Stack({ alignContent: Alignment.TopEnd }) {      if (this.isCategoryMode) {        CategoryModeFoods()      } else {        ListModeFoods()      }      Row() {        Image($r("app.media.ic_switch"))          .height(24)          .width(24)          .margin({ left: 24, right: 24 })          .focusable(true)      }      .height(56)      .backgroundColor(this.getMoreIconBgColor())      .stateStyles({        focused: {          .border({            radius: $r('sys.float.ohos_id_corner_radius_clicked'),            color: $r('sys.color.ohos_id_color_focused_outline'),            style: BorderStyle.Solid          })        },        normal: {          .border({            radius: $r('sys.float.ohos_id_corner_radius_clicked'),            width: 0          })        }      })      .onFocus(() => this.isMoreIconOnFocus = true)      .onBlur(() => this.isMoreIconOnFocus = false)      .onHover((isOn) => this.isMoreIconOnHover = isOn)      .onClick(() => {        this.isCategoryMode = !this.isCategoryMode      })    }  }}@Entry@Componentstruct Home {  @State currentTabIndex: number = 0  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'  private breakpointSystem: BreakpointSystem = new BreakpointSystem()  /* *   * 主页和记录的tabs   * */  @Builder  bottomBarItemBuilder(name: Resource, icon: Resource, index: number) {    Flex({      direction: new BreakPointType({        sm: FlexDirection.Column,        md: FlexDirection.Row,        lg: FlexDirection.Column      }).getValue(this.currentBreakpoint),      justifyContent: FlexAlign.Center,      alignItems: ItemAlign.Center    }) {      Image(icon)        .height(24)        .width(24)        .fillColor(this.getTabBarColor(index))      Text(name)        .margin(new BreakPointType<Padding>({          sm: { top: 4 },          md: { left: 8 },          lg: { top: 4 }        }).getValue(this.currentBreakpoint) as Padding)        .fontSize(11)        .fontColor(this.getTabBarColor(index))    }  }  aboutToAppear() {    this.breakpointSystem.register()  }  aboutToDisappear() {    this.breakpointSystem.unregister()  }  build() {    Tabs({      barPosition: new BreakPointType({        sm: BarPosition.End,        md: BarPosition.End,        lg: BarPosition.Start      }).getValue(this.currentBreakpoint)    }) {      TabContent() {        FoodsDisplay()      }.tabBar(this.bottomBarItemBuilder($r("app.string.tab_bar_home"), $r("app.media.ic_bottom_home"), 0))      TabContent() {        Records()      }.tabBar(this.bottomBarItemBuilder($r("app.string.tab_bar_record"), $r("app.media.ic_bottom_record"), 1))      TabContent() {        PersonalCenter()      }.tabBar(this.bottomBarItemBuilder($r("app.string.tab_bar_me"), $r("app.media.ic_public_me"), 2))    }    .vertical(new BreakPointType({ sm: false, md: false, lg: true }).getValue(this.currentBreakpoint) as boolean)    .barWidth(new BreakPointType({ sm: '100%', md: '100%', lg: '56vp' }).getValue(this.currentBreakpoint) as string)    .barHeight(new BreakPointType({ sm: '56vp', md: '56vp', lg: '60%' }).getValue(this.currentBreakpoint) as string)    .animationDuration(300)    .onChange((index) => {      this.currentTabIndex = index    })  }  private getTabBarColor(index: number) {    return this.currentTabIndex == index ? $r('app.color.tab_bar_select_color') : $r('app.color.tab_bar_normal_color')  }}
复制代码
6、另外一个思绪实现上拉加载,下拉革新

根据List中的回调方法onScrollIndex()监听当前线表首尾索引,根据触摸事件onTouch()处置惩罚下拉和上拉。

  1. const TopHeight = 200;
  2. @Entry
  3. @Component
  4. struct Index {
  5.   @State list: Array<number> = []
  6.   // 列表y坐标偏移量
  7.   @State offsetY: number = 0
  8.   // 按下的y坐标
  9.   private downY = 0
  10.   // 上一次移动的y坐标
  11.   private lastMoveY = 0
  12.   // 当前列表首部的索引
  13.   private startIndex = 0
  14.   // 当前列表尾部的索引
  15.   private endIndex = 0
  16.   // 下拉刷新的布局高度
  17.   private pullRefreshHeight = 70
  18.   // 下拉刷新文字:下拉刷新、松开刷新、正在刷新、刷新成功
  19.   @State pullRefreshText: string= '下拉刷新'
  20.   // 下拉刷新图标:与文字对应
  21.   @State pullRefreshImage: Resource = $r("app.media.ic_pull_refresh_down")
  22.   // 是否可以刷新:未达到刷新条件,收缩回去
  23.   private isCanRefresh = false
  24.   // 是否正在刷新:刷新中不进入触摸逻辑
  25.   private isRefreshing: boolean = false
  26.   // 是否已经进入了下拉刷新操作
  27.   private isPullRefreshOperation = false
  28.   // 上拉加载的布局默认高度
  29.   private loadMoreHeight = 70
  30.   // 上拉加载的布局是否显示
  31.   @State isVisibleLoadMore: boolean = false
  32.   // 是否可以加载更多
  33.   private isCanLoadMore = false
  34.   // 是否加载中:加载中不进入触摸逻辑
  35.   private isLoading: boolean = false
  36.   // 自定义下拉刷新布局
  37.   @Builder CustomPullRefreshLayout(){
  38.     Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
  39.       Image(this.pullRefreshImage)
  40.         .width(18)
  41.         .height(18)
  42.       Text(this.pullRefreshText)
  43.         .margin({ left: 7, bottom: 1 })
  44.         .fontSize(17)
  45.     }
  46.     .width('100%')
  47.     .height(this.pullRefreshHeight)
  48.     // 布局跟着列表偏移量移动
  49.     .offset({ x: 0, y: `${vp2px(-this.pullRefreshHeight) + this.offsetY}px` })
  50.   }
  51.   // 自定义加载更多布局
  52.   @Builder CustomLoadMoreLayout(){
  53.     Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
  54.       Image($r("app.media.ic_loading"))
  55.         .width(18)
  56.         .height(18)
  57.       Text('加载更多中...')
  58.         .margin({ left: 7, bottom: 1 })
  59.         .fontSize(17)
  60.     }
  61.     .width('100%')
  62.     .height(this.loadMoreHeight)
  63.     .backgroundColor('#f4f4f4')
  64.     .visibility(this.isVisibleLoadMore ? Visibility.Visible : Visibility.None)
  65.   }
  66.   // 刷新测试数据
  67.   private refreshData(){
  68.     this.list = []
  69.     for (var i = 0; i < 15; i++) {
  70.       this.list.push(i)
  71.     }
  72.   }
  73.   // 加载更多测试数据
  74.   private loadMoreData(){
  75.     let initValue = this.list[this.list.length-1] + 1
  76.     for (var i = initValue; i < initValue + 10; i++) {
  77.       this.list.push(i)
  78.     }
  79.   }
  80.   build() {
  81.     Column() {
  82.       // 下拉刷新布局
  83.       this.CustomPullRefreshLayout()
  84.       // 列表布局
  85.       List() {
  86.         ForEach(this.list, item => {
  87.           ListItem() {
  88.             Column() {
  89.               Text(`Item ${item}`)
  90.                 .padding(15)
  91.                 .fontSize(18)
  92.             }
  93.           }
  94.         }, item => item.toString())
  95.         // 加载更多布局
  96.         ListItem(){
  97.           this.CustomLoadMoreLayout()
  98.         }
  99.       }
  100.       .backgroundColor(Color.White) // 背景
  101.       .divider({ color: '#e2e2e2', strokeWidth: 1 }) // 分割线
  102.       .edgeEffect(EdgeEffect.None) // 去掉回弹效果
  103.       .offset({ x: 0, y: `${this.offsetY - TopHeight}px` }) // touch事件计算的偏移量单位是px,记得加上单位
  104.       .onScrollIndex((start, end) => { // 监听当前列表首位索引
  105.         console.info(`${start}=start============end=${end}`)
  106.         this.startIndex = start
  107.         this.endIndex = end
  108.       })
  109.     }
  110.     .width('100%')
  111.     .height('100%')
  112.     .backgroundColor('#f4f4f4')
  113.     .onTouch((event) => this.listTouchEvent(event))// 父容器设置touch事件,当列表无数据也可以下拉刷新
  114.     .onAppear(() => {
  115.       this.refreshData()
  116.     })
  117.   }
  118.   // 触摸事件
  119.   listTouchEvent(event: TouchEvent){
  120.     switch (event.type) {
  121.       case TouchType.Down: // 手指按下
  122.       // 记录按下的y坐标
  123.         this.downY = event.touches[0].y
  124.         this.lastMoveY = event.touches[0].y
  125.         break
  126.       case TouchType.Move: // 手指移动
  127.       // 下拉刷新中 或 加载更多中,不进入处理逻辑
  128.         if(this.isRefreshing || this.isLoading){
  129.           console.info('========Move刷新中,返回=========')
  130.           return
  131.         }
  132.       // 判断手势
  133.         let isDownPull = event.touches[0].y - this.lastMoveY > 0
  134.       // 下拉手势 或 已经进入了下拉刷新操作
  135.         if ((isDownPull || this.isPullRefreshOperation) && !this.isCanLoadMore) {
  136.           this.touchMovePullRefresh(event)
  137.         } else {
  138.           this.touchMoveLoadMore(event)
  139.         }
  140.         this.lastMoveY = event.touches[0].y
  141.         break
  142.       case TouchType.Up: // 手指抬起
  143.       case TouchType.Cancel: // 触摸意外中断:来电界面
  144.       // 刷新中 或 加载更多中,不进入处理逻辑
  145.         if(this.isRefreshing || this.isLoading){
  146.           console.info('========Up刷新中,返回=========')
  147.           return
  148.         }
  149.         if (this.isPullRefreshOperation) {
  150.           this.touchUpPullRefresh()
  151.         } else {
  152.           this.touchUpLoadMore()
  153.         }
  154.         break
  155.     }
  156.   }
  157.   //============================================下拉刷新==================================================
  158.   // 手指移动,处理下拉刷新
  159.   touchMovePullRefresh(event:TouchEvent){
  160.     // 当首部索引位于0
  161.     if (this.startIndex == 0) {
  162.       this.isPullRefreshOperation = true
  163.       // 下拉刷新布局高度
  164.       var height = vp2px(this.pullRefreshHeight)
  165.       // 滑动的偏移量
  166.       this.offsetY = event.touches[0].y - this.downY
  167.       // 偏移量大于下拉刷新布局高度,达到刷新条件
  168.       if (this.offsetY >= height) {
  169.         // 状态1:松开刷新
  170.         this.pullRefreshState(1)
  171.         // 偏移量的值缓慢增加
  172.         this.offsetY = height + this.offsetY * 0.15
  173.       } else {
  174.         // 状态0:下拉刷新
  175.         this.pullRefreshState(0)
  176.       }
  177.       if (this.offsetY < 0) {
  178.         this.offsetY = 0
  179.         this.isPullRefreshOperation = false
  180.       }
  181.     }
  182.   }
  183.   // 手指抬起,处理下拉刷新
  184.   touchUpPullRefresh(){
  185.     // 是否可以刷新
  186.     if (this.isCanRefresh) {
  187.       console.info('======执行下拉刷新========')
  188.       // 偏移量为下拉刷新布局高度
  189.       this.offsetY = vp2px(this.pullRefreshHeight)
  190.       // 状态2:正在刷新
  191.       this.pullRefreshState(2)
  192.       // 模拟耗时操作
  193.       setTimeout(() => {
  194.         this.refreshData()
  195.         this.closeRefresh()
  196.       }, 2000)
  197.     } else {
  198.       console.info('======关闭下拉刷新!未达到条件========')
  199.       // 关闭刷新
  200.       this.closeRefresh()
  201.     }
  202.   }
  203.   // 下拉刷新状态
  204.   // 0下拉刷新、1松开刷新、2正在刷新、3刷新成功
  205.   pullRefreshState(state:number){
  206.     switch (state) {
  207.       case 0:
  208.       // 初始状态
  209.         this.pullRefreshText = '下拉刷新'
  210.         this.pullRefreshImage = $r("app.media.ic_pull_refresh_down")
  211.         this.isCanRefresh = false
  212.         this.isRefreshing = false
  213.         break;
  214.       case 1:
  215.         this.pullRefreshText = '松开刷新'
  216.         this.pullRefreshImage = $r("app.media.ic_pull_refresh_up")
  217.         this.isCanRefresh = true
  218.         this.isRefreshing = false
  219.         break;
  220.       case 2:
  221.         this.offsetY = vp2px(this.pullRefreshHeight)
  222.         this.pullRefreshText = '正在刷新'
  223.         this.pullRefreshImage = $r("app.media.ic_loading")
  224.         this.isCanRefresh = true
  225.         this.isRefreshing = true
  226.         break;
  227.       case 3:
  228.         this.pullRefreshText = '刷新成功'
  229.         this.pullRefreshImage = $r("app.media.ic_refresh_succeed")
  230.         this.isCanRefresh = true
  231.         this.isRefreshing = true
  232.         break;
  233.     }
  234.   }
  235.   // 关闭刷新
  236.   closeRefresh() {
  237.     // 如果允许刷新,延迟进入,为了显示刷新中
  238.     setTimeout(() => {
  239.       var delay = 50
  240.       if (this.isCanRefresh) {
  241.         // 状态3:刷新成功
  242.         this.pullRefreshState(3)
  243.         // 为了显示刷新成功,延迟执行收缩动画
  244.         delay = 500
  245.       }
  246.       animateTo({
  247.         duration: 150, // 动画时长
  248.         delay: delay, // 延迟时长
  249.         onFinish: () => {
  250.           // 状态0:下拉刷新
  251.           this.pullRefreshState(0)
  252.           this.isPullRefreshOperation = false
  253.         }
  254.       }, () => {
  255.         this.offsetY = 0
  256.       })
  257.     }, this.isCanRefresh ? 500 : 0)
  258.   }
  259.   //============================================加载更多==================================================
  260.   // 手指移动,处理加载更多
  261.   touchMoveLoadMore(event:TouchEvent) {
  262.     // 因为加载更多是在列表后面新增一个item,当一屏能够展示全部列表,endIndex 为 length+1
  263.     if (this.endIndex == this.list.length - 1 || this.endIndex == this.list.length) {
  264.       // 滑动的偏移量
  265.       this.offsetY = event.touches[0].y - this.downY
  266.       if (Math.abs(this.offsetY) > vp2px(this.loadMoreHeight)/2) {
  267.         // 可以刷新了
  268.         this.isCanLoadMore = true
  269.         // 显示加载更多布局
  270.         this.isVisibleLoadMore = true
  271.         // 偏移量缓慢增加
  272.         this.offsetY = - vp2px(this.loadMoreHeight) + this.offsetY * 0.1
  273.       }
  274.     }
  275.   }
  276.   // 手指抬起,处理加载更多
  277.   touchUpLoadMore() {
  278.     animateTo({
  279.       duration: 200, // 动画时长
  280.     }, () => {
  281.       // 偏移量设置为0
  282.       this.offsetY = 0
  283.     })
  284.     if (this.isCanLoadMore) {
  285.       console.info('======执行加载更多========')
  286.       // 加载中...
  287.       this.isLoading = true
  288.       // 模拟耗时操作
  289.       setTimeout(() => {
  290.         this.closeLoadMore()
  291.         this.loadMoreData()
  292.       }, 2000)
  293.     } else {
  294.       console.info('======关闭加载更多!未达到条件========')
  295.       this.closeLoadMore()
  296.     }
  297.   }
  298.   // 关闭加载更多
  299.   closeLoadMore() {
  300.     this.isCanLoadMore = false
  301.     this.isLoading = false
  302.     this.isVisibleLoadMore = false
  303.   }
  304. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

涛声依旧在

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表