IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【小程序】支付宝小程序自定义下拉刷新组件 -> 正文阅读

[移动开发]【小程序】支付宝小程序自定义下拉刷新组件

🌾 用支付宝原生框架写一个下拉刷新组件。

背景

支付宝提供的my.startPullDownRefreshAPI是下拉整个页面的,当有固定头部的时候,就达不到要求。而MAS提供的adc-flat-list组件可以支持固定头部,但是不能支持有底部,如果将底部放在组件外,就会出现两个滚动条。
后来参考了些博客,整理了这个满足业务需求的下拉刷新组件,参考文章最后贴出。

实现效果

在这里插入图片描述

子组件

组件命名为pull-refresh

<view class="pull-refresh-cont">
  <!-- 插槽 - 固定的头部 -->
  <slot name="header" />
  <scroll-view class="scroll-view" scroll-y='{{!isindrag}}' onScroll='scorll'>
    <view class='column' onTouchStart='start' onTouchEnd='end' onTouchMove='move'>
      <view a:if="{{refreshBatch}}">
        <view style='height:{{hei}}px;' class='refresh'>{{desc}}</view>
      </view>
      <!-- 插槽 - 列表内容 -->
      <slot />
      <!-- 插槽 - 跟在列表后面的底部内容 -->
      <slot name="footer" />
    </view>
  </scroll-view>
</view>

.pull-refresh-cont {
  display: flex;
  flex-direction: column;
  box-sizing: content-box;
  // 兼容底部安全距离
  height: calc(100vh - constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
  height: calc(100vh - env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
  overflow: hidden;
  background-color: #fff;
  
  .scroll-view {
    flex: 1;
    
    .column {
      height: 100%;
      
      .refresh {
        background-color: #f5f5f5;
        text-align: center;
        padding: 10rpx 0;
      }
    }
  }
}

import { createComponent } from '@whale.io/mini/es/miniu';

interface IProps {
  onRefresh: () => Promise<unknown>;
}

interface Data {
  desc?: string;
  hei: number;
  scrolltop: number;
  isindrag?: boolean;
  refreshBatch?: boolean;
  sy: number;
}

Component(
  createComponent<IProps, Data>({
    props: {
      onRefresh: () => new Promise<void>(executor => executor())
    },
    data: {
      // 刷新提示语
      desc: '🦘下拉刷新',
      // 刷新view高度阈值
      hei: 0,
      // scorll-view滑动离顶部的距离
      scrolltop: 0,
      // 是否在下拉状态(必须要滑动到顶部才能触发)
      isindrag: false,
      sy: 0,
      refreshBatch: false
    },
    methods: {
      saveRef (ref) {
        this.$headerRef = ref;
      },
      start (e) {
        //  记录手指触摸是的y坐标
        this.data.sy = e.touches[0].clientY;
      },
      move (e) {
        //  计算手指滑动的距离
        const delta = e.touches[0].clientY - this.data.sy;
        //  scorll-view滑动到顶部且继续向上滑动时,走scorll-view滑动流程
        if (this.data.hei <= 0 && delta <= 0) {
          return;
        }
        //  scorll-view已经滑动到顶部,继续下拉进入下拉状态
        if (this.data.scrolltop <= 0) {
          if (this.data.isindrag === false) {
            this.setData({
              isindrag: true,
              refreshBatch: true
            });
          }
          let tempdelta = 0;
          if (delta > 0) {
            //  手指向下滑动
            if (this.data.hei > 50) {
              //  触发阈值,更改状态
              this.setData({
                desc: '🙆?♀?松开刷新'
              });
              // 增大下拉阻尼感
              tempdelta = this.data.hei + delta / (this.data.hei - 50);
            } else {
              this.setData({
                desc: '🦘下拉刷新'
              });
              //  手指移动未到阈值,按正常滑动增加高度
              tempdelta = this.data.hei + delta;
            }
          } else {
            //  手指向上滑动
            tempdelta = this.data.hei + delta;
            //  刷新状态view最小为0
            if (tempdelta <= 0) {
              tempdelta = 0;
            }
            this.setData({
              desc: '🦘下拉刷新'
            });
          }
          //  滑动完成设置刷新view高度
          this.setData({
            hei: tempdelta
          });
        }
        //  每次滑动事件后记录y坐标
        this.data.sy = e.touches[0].clientY;
      },
      end () {
        //  控制刷新文字的显示隐藏
        this.setData({
          refreshBatch: false
        });
        //  手指离开时,如果阈值大于等于50,则触发刷新
        if (this.data.hei >= 50) {
          this.setData({
            desc: '😆正在刷新...',
            hei: 50,
            refreshBatch: true
          });
          //  加载内容信息
          this.props.onRefresh().then(() => {
            this.setData({
              desc: '🌻刷新成功'
            });
            this.reset();
          }).catch(() => {
            this.setData({
              desc: '😭刷新失败'
            });
            this.reset();
          });
        } else {
          //  未下拉到阈值,松开时则收起刷新view
          this.data.sy = 0;
          this.setData({
            desc: '🦘下拉刷新',
            hei: 0,
            isindrag: false,
            scrolltop: 0
          });
        }
      },
      scorll (e) {
        // 未进入下拉状态时,记录scorll-view滑动距离顶部的距离
        const st = e.detail.scrollTop;
        if (this.data.isindrag === false) {
          this.setData({
            scrolltop: st
          });
        }
      },
      // 还原刷新状态
      reset () {
        setTimeout(() => {
          this.data.sy = 0;
          this.setData({
            desc: '🦘下拉刷新',
            hei: 0,
            isindrag: false,
            scrolltop: 0,
            // 暂时
            refreshBatch: false
          });
        }, 500);
      }
    }
  })
);

父组件

在页面test中引用pull-refresh组件

<pull-refresh
  onRefresh="onRefresh"
>
  <!-- 固定头部 -->
  <view slot="header" style="backgroundColor:pink;">
    欢迎使用XXX系统!
  </view>

  <!-- 中间列表 -->
  <view a:if="{{list && list.length !== 0}}">
    <view a:for="{{list}}">
      {{item}}
    </view>
  </view>
  <view a:else>
    lalalalalal暂无数据
  </view>

  <!-- 紧随列表的底部 -->
  <view slot="footer" style="textAlign:center;marginTop:50rpx;">
    -- 我是有底线的 --
  </view>
</pull-refresh>

{
  "defaultTitle": "",
  "usingComponents": {
    "pull-refresh": "../../components/pull-refresh/index"
  }
}

import { createPage } from '@whale.io/mini/es/miniu';

interface Data {
  list: string[];
}

interface This {
  getData: (flag?: boolean) => Promise<Record<string, unknown>[] | string[]>;
  onRefresh: () => void;
}

Page(createPage<Data, This>({
  data: {
    list: []
  },
  onLoad () {
    const list: string[] = [];
    for (let i = 0; i < 50; i++) {
      list.push(`data--${i}`);
    }
    this.setData({
      list
    });
  },
  // 模拟接口请求,flag为true请求成功,false请求失败
  getData (flag = false) {
    return new Promise((resolve, reject) => {
      const list: string[] = [];
      my.showToast({
        content: '正在加载'
      });
      if (flag) {
        for (let i = 0; i < 100; i++) {
          list.push(`新data--${i}`);
        }
        // 模拟接口返回数据
        setTimeout(() => {
          this.setData({
            list
          });
          resolve(list);
          my.hideToast();
        }, 2000);
      } else {
        setTimeout(() => {
          this.setData({
            list
          });
          my.hideToast();
          reject(new Error('报错啦'));
        }, 2000);
      }
    });
  },
  // 子组件根据onRefresh返回的结果提示刷新成功/失败,还原刷新效果
  async onRefresh () {
    const res = await this.getData(true);
    return res;
  }
}));

【参考】
https://blog.51cto.com/u_15102934/2632425
https://www.it610.com/article/1282381264719986688.htm
https://blog.csdn.net/zxj000830/article/details/115916757

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:47:44  更:2022-04-22 18:47:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 22:00:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码