「wx」微信小程序自定義下拉刷新

tags: [wx]
categories: [微信小程序]
cover:


需求:

在小程序內存在列表等形式的頁面內增加下拉刷新功能,提高用戶體驗感,加強界面操作與交互性;

實現方法:

1、小程序提供的下拉刷新(無法自定義刷新動畫)

  • 在頁面設置內開啓下拉(單獨頁面設置);
{
  "enablePullDownRefresh": true,
}
  • 使用onPullDownRefresh()監聽用戶下拉操作,實現刷新操作;
  • 也可以通過wx.startPullDownRefreshwx.stopPullDownRefresh觸發和關閉頁面下拉刷新;

可能遇到的問題:
1)下拉時沒有出現刷新的點點動畫
可能是因爲設置的頁面背景色與點點動畫一致導致無法看到動畫,可以在頁面配置中配置背景文字顏色:

{
  "backgroundTextStyle": "dark"
}


2、scroll-view內refresher-enabled屬性開啓自定義刷新
基本庫要求:2.10.1

refresher-enabled boolean false 開啓自定義下拉刷新 2.10.1
refresher-threshold number 45 設置自定義下拉刷新閾值 2.10.1
refresher-default-style string “black” 設置自定義下拉刷新默認樣式,支持設置 black/white/none, none 表示不使用默認樣式 2.10.1
refresher-background string “#FFF” 設置自定義下拉刷新區域背景顏色 2.10.1
refresher-triggered boolean false 設置當前下拉刷新狀態,true 表示下拉刷新已經被觸發,false 表示下拉刷新未被觸發 2.10.1
bindrefresherpulling eventhandle 自定義下拉刷新控件被下拉 2.10.1
bindrefresherrefresh eventhandle 自定義下拉刷新被觸發 2.10.1
bindrefresherrestore eventhandle 自定義下拉刷新被複位 2.10.1
bindrefresherabort eventhandle 自定義下拉刷新被中止 2.10.1


官方文檔:scroll-view


3、原始scroll-view自定義下拉實現(爲兼容2.10.1一下的下拉刷新
通過監聽手指移動距離控制需要下拉模塊的下拉距離,主要事件bindtouchstart,bindtouchmove和bindtouchend,bindtouchmove記錄手指開始下拉時的起始位置,bindtouchmove計算下拉距離,bindtouchend判斷並實現刷新方法。
我的自定義下拉組件(Taro框架)

import Taro, { Component, render } from "@tarojs/taro";
import { View, Image, ScrollView } from "@tarojs/components";
import { getGlobalData } from "../../state/global";
import util from "../../utils";
import "./index.less";

const rpx2px = util.rpx2px();
export default class Loading extends Component {
  config = {
    enablePullDownRefresh: false,
    disableScroll: true
  };
  constructor() {
    super(...arguments);
    this.state = {
      now_scroll_top: 0,
      moveStartPosition: 0, //開始位置
      moveDistance: 0, //移動距離
      moveRefreshDistance: rpx2px(136), //達到刷新的閾值,沒有達到不進行刷新並回彈
      moveMaxDistance: rpx2px(220), //最大可滑動距離
      isRefreshMaxDown: false, //是否達到了最大距離, 用來判斷是否要震動提示
      loading: false, //是否正在loading
      loading_scale: 0,
      // 2020.03.03 動態分成兩個階段,下拉時展示小鴨子浮動,放手後加載中鱷魚
      pull_moving: false,
      back_top_num: ""
    };
  }

  componentWillReceiveProps(nextProps) {
    // 父組件觸發刷新結束/取消刷新
    // console.log(nextProps, this.state.loading)
    if (nextProps.refreshLoading != this.state.loading) {
      if(nextProps.refreshLoading) {
        this.setState({
          loading: nextProps.refreshLoading
        });
      } else {
        this.stopPullLoading();
      }
    }
  }

  render() {
    let {
      now_scroll_top,
      moveDistance,
      loading_scale,
      loading,
      pull_moving,
      back_top_num
    } = this.state;
    let { autoHeight } = this.props;
    let sys_info = getGlobalData("system_info");
    return (
      <ScrollView
        className={["view-component component-pull-scroll", autoHeight ? "scroll-auto" : ""]}
        id="pull-scroll-view"
        style={{ height: autoHeight ? 'auto' : (sys_info.screenHeight - 80 + "px") }}
        scrollY={true}
        scrollTop={back_top_num}
        data-nowScrollTop={now_scroll_top}
        onTouchStart={this.pullTouchStart.bind(this)}
        onTouchMove={this.pullTouchMove.bind(this)}
        onTouchEnd={this.pullTouchEnd.bind(this)}
        onScroll={this.pullScroll.bind(this)}
        scrollWithAnimation
      >
        <View
          className="pull-view-container"
          style={{ transform: "translateY(" + moveDistance + "px)" }}
        >
          <View
            className="pull-refresh-box"
            style={{
              transform: "scale(" + loading_scale + ") translateY(-100%)"
            }}
          >
            {(pull_moving || loading) && (
              <Image
                className="loading-img"
                src="http://assets-cdn.lanqb.com/manual-box/mp/mp-pullLoading-duck.gif"
              ></Image>
            )}
          </View>
          <slot></slot>
        </View>
      </ScrollView>
    );
  }

  /**
   * 觸摸事件開始
   * @param e
   */
  pullTouchStart(e) {
    let {
      moveDistance,
      moveStartPosition,
      loading,
      now_scroll_top
    } = this.state;
    if (loading || now_scroll_top >= 30) return;
    moveDistance = 0; //重置移動距離
    moveStartPosition = e.touches[0].clientY; //記錄開始移動的位置
    this.setState({
      moveDistance,
      moveStartPosition
    });
  }
  /**
   * 觸摸事件移動
   * @param e
   */
  pullTouchMove(e) {
    const _this = this;
    let {
      loading,
      moveDistance,
      moveStartPosition,
      moveMaxDistance,
      isRefreshMaxDown,
      loading_scale,
      moveRefreshDistance,
      pull_moving,
      now_scroll_top
    } = this.state;
    //如果正在loading,則不進行接下來的行爲
    if (loading || now_scroll_top >= 30) return; 
    //當前scroll-view所在的滾動位置
    let nowScrollTop = e.currentTarget.dataset.nowscrolltop;

    //開始計算移動距離
    moveDistance = e.touches[0].clientY - moveStartPosition;

    //如果是往下滑動,則阻止接下來的行爲
    if (moveDistance <= 0) {
      loading_scale = 0;
      // _this.stopPullLoading();
    } else {
      // if (nowScrollTop !== 0) {
      //   // 如果滾動高度 !== 0 則不進行任何操作
      // } else {
        if (moveDistance > moveMaxDistance) {
          //達到閾值
          // 顯示刷新動畫
          pull_moving = true;
          loading_scale = 1;
          loading = false;
          moveDistance = moveMaxDistance;
          //觸發一次震動
          if (!isRefreshMaxDown) {
            isRefreshMaxDown = true;
            Taro.vibrateShort();
          }
        } else if (moveDistance < moveRefreshDistance) {
          loading_scale = 0;
          loading = false;
          pull_moving = false;
        } else {
          loading = false;
          pull_moving = true;
          loading_scale =
            moveDistance / rpx2px(136) > 1 ? 1 : moveDistance / rpx2px(136);
        }
      // }
    }
    this.setState({
      moveDistance,
      isRefreshMaxDown,
      loading_scale,
      pull_moving
    });
  }
  /**
   * 結束觸摸事件
   */
  pullTouchEnd(e) {
    const _this = this;
    let {
      loading,
      moveDistance,
      moveRefreshDistance,
      loading_scale,
      moveMaxDistance,
      pull_moving,
      now_scroll_top
    } = this.state;
    if (loading || now_scroll_top >= 30) return;

    //當前scroll-view所在的滾動位置
    let nowScrollTop = e.currentTarget.dataset.nowscrolltop;

    //如果是往下滑動,則阻止接下來的行爲
    if (moveDistance <= 0) {
      // return false
      loading_scale = 0;
      moveDistance = 0;
      loading = false;
      pull_moving = false;
    } else {
        if (moveDistance < moveRefreshDistance) {
          // 移動距離小於刷新閾值,不進行刷新
          loading_scale = 0;
          moveDistance = 0;
          loading = false;
          pull_moving = false;
          console.log("需要回彈");
        } else {
          // 開始刷新
          loading = true;
          pull_moving = false;
          _this.pullRefresh();
          if (moveDistance >= moveMaxDistance) {
            moveDistance = moveRefreshDistance;
            loading_scale =
              moveDistance / rpx2px(136) > 1 ? 1 : moveDistance / rpx2px(136);
          }
        }
      // }
    }
    this.setState({
      loading,
      //重置
      moveStartPosition: 0,
      isRefreshMaxDown: false,
      loading_scale,
      moveDistance,
      pull_moving
    });
  }
  pullRefresh() {
    const _this = this;
    this.props.onPullRefresh();
  }
  pullScroll(event) {
    let { scrollTop } = event.detail;
    this.setState({
      now_scroll_top: scrollTop,
      back_top_num: ""
    });
  }
  stopPullLoading() {
    const _this = this;
    setTimeout(function() {
      _this.setState({
        moveDistance: 0,
        moveStartPosition: 0,
        isRefreshMaxDown: false,
        pull_moving: false,
        back_top_num: 0,
        now_scroll_top: 0
      });
      setTimeout(function() {
        _this.setState({          
          loading_scale: 0,
          loading: false,
        })
      }, 1500)
    }, 2000)
  }
}

部分問題:

1、與ios上橡皮筋效果衝突導致下拉無法觸發自定義刷新
頁面配置disabledScroll,禁止頁面滾動,同時頁面內的列表滾動需要自己再優化調整;


2、scroll-view的scroll問題
需要設定固定高度然後縱向滾動。[我都忘了是啥問題了……


3、頁面下拉刷新結束後再滑動列表出現閃屏
在禁止頁面橡皮筋效果後,如果頁面內存在需滾動區域使用scroll-view效果比view更加流暢;
不設定固定高度不會發生閃屏但是頁面滾動非常不流暢;


4、使用官方提供的scroll-view自定義的動畫時,當scroll-view內容不足充滿一屏時下拉出現問題
將scroll-view設置固定高度後,將其子元素的高度設置多一像素達到隱形撐滿的效果。

<ScrollView
	className={["view-component component-pull-scroll", autoHeight ? "scroll-auto" : ""]}
	id="pull-scroll-view"
	style={{ height: (sys_info.screenHeight + "px") }}
>
	<View
		className="pull-view-container"
		style={{ height: (sys_info.screenHeight + 1 + "px") }}
	>...</View>
</ScrollView>


5、scroll-view內fixed元素問題
ios內scroll-view內fixed元素層級會出現問題,可能出現被遮擋的問題。


6、當頁面內局部需要下拉刷新時可能導致內外兩個滾動條問題
一個是頁面滾動條一個是scroll-view滾動條,由於操作的時候觸發的是scroll-view部分的滾動導致頁面滾動無法進行從而影響頁面其他操作。
Q5和Q6可以合併成一個問題,當頁面需要一個吸頂操作時,即滑動距離超過閾值時導航條吸頂的功能,若scroll-view將整個頁面包含就會出現Q5的問題,可能導致在ios內吸頂的導航欄無法顯示,若scroll-view只包含需要刷新的部分則會出現Q6的兩個滾動條的問題。
1)在頁面未觸發吸頂時禁止scroll-view模塊下拉,觸發後放開滾動,同時會導致無法下拉。
2)頁面滾動觸發,scroll-view模塊可下拉,但是滾動區域無法滾動,且下拉動畫只顯示一次。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章