react + better_scroll 简单封装

utils

import BScroll from '@better-scroll/core'  // BScroll 核心
import MouseWheel from '@better-scroll/mouse-wheel'  // 引入滚轮
import PullDown from '@better-scroll/pull-down'  // 上拉
import Scrollbar from '@better-scroll/scroll-bar'

BScroll.use(MouseWheel)  // 下载滚轮插件
BScroll.use(PullDown)  // 下载上拉 
BScroll.use(Scrollbar)

const TIME_BOUNCE = 800  // 上拉弹跳时间
const REQUEST_TIME = 1000  // 请求处理时间
const THRESHOLD = 100  // 上拉高度执行
const STOP = 56  // 弹跳高度
let STEP = 0  // 页码

// Y轴滚动
export const BS = (wrapper, setBsContainer = false, action = false,) =>
{
    const { payload } = action
    if (wrapper)
    {
        if (typeof wrapper === 'string')
        {
            let wrapperRef = document.querySelector('#' + wrapper)
            deploy(wrapperRef ? wrapperRef : null, setBsContainer ? setBsContainer : null, payload ? payload.callback : null)
        } else
        {
            deploy(wrapper ? wrapper : null, setBsContainer ? setBsContainer : null, payload ? payload.callback : null)
        }
    }
}
// Y轴配置
const deploy = (wrapper, setBsContainer, callback) =>
{
    let bs = new BScroll(wrapper, {
        probeType: 3,  // 默认0 不侦测 0和1 都不侦测  2:在手指滚动的过程中侦测 手指离开后的惯性滚动过程中不侦测 3:只要是滚动 都进行侦测
        click: true,  // 可以点击  用法:要覆盖本机滚动,BetterScroll必须禁止某些默认浏览器行为,例如鼠标单击。如果您希望您的应用程序响应click事件,则必须将该选项显式设置为true。然后BetterScroll将向其_constructed调度事件添加一个私有属性,该属性的值为true。
        pullDownRefresh: true,  // 向上拉 可以上拉 请求数据 加载更多
        mouseWheel: true,  // 鼠标滚轮
        bounceTime: TIME_BOUNCE,  // 上拉弹跳时间
        pullDownRefresh: {
            threshold: THRESHOLD,
            stop: STOP
        }
    })

    if (setBsContainer) setBsContainer(bs)  // 用于保存 bs 对象

    bs.on('scroll', position =>
    {
        if (callback)
        {
            const { scrolled } = callback
            scrolled(position, bs) // 发送两个参数 position 位置  bs 对象
        }
        // 默认情况下 BScroll 是不可以实时监听滚动位置
        // console.log(position); // 实时滚动的位置
    })
    bs.on('pullingDown', () =>
    {
        // 触发时机:在一次上拉加载的动作后 这个时机一般用来去后端请求数据
        // bs.finishPullUp()
        // 注意 只能回调一次
        console.log('上拉加载更多');
        // 先去发送网络请求 请求更多页的数据

        // 等数据请求完成 并将数据展示出来
        setTimeout(() =>
        {
            bs.finishPullDown()  // 再次下拉
            if (callback)
            {
                const PullBack = [TIME_BOUNCE, REQUEST_TIME, THRESHOLD, STOP, STEP]
                const { pullUp } = callback
                pullUp(PullBack, '上拉后发送的数据')
            }
            // bs.refresh()
            // bs.finishPullUp()   // 可以再次执行
        }, 1500)  // 两秒拉一次
    })
    bs.on('scrollEnd', e =>
    {
        console.log('滚动结束');
    })
}




// X轴滚动 container 两个参数 一个 wrapper 一个滚动条的 ref  setDsContainer 用来保存 DS action 暂时没写
export const DS = (container, setDsContainer = false, action = false) =>
{

    const { wrapper, scrollBar } = container  // wrapper 可以是 id scrollBar 必须是 Ref
    const { payload } = action
    console.log(wrapper, 'data');
    if (wrapper)
    {
        if (typeof wrapper === 'string')
        {
            let wrapperRef = document.querySelector('#' + wrapper)
            deployX(wrapper ? [wrapperRef, scrollBar] : null, setDsContainer ? setDsContainer : null, payload ? payload.callback : null)
        } else
        {
            deployX(wrapper ? [wrapper, scrollBar] : null, setDsContainer ? setDsContainer : null, payload ? payload.callback : null)
        }
    }
}
// X轴配置
const deployX = (container, setDsContainer) =>  // horizontal 拿到自定义滚动条 ref 
{
    let [wrapper, horizontal] = container  // 结构赋值
    if (wrapper)
    {
        let ds = new BScroll(wrapper, {
            probeType: 0,  // 默认0 不侦测 0和1 都不侦测  2:在手指滚动的过程中侦测 手指离开后的惯性滚动过程中不侦测 3:只要是滚动 都进行侦测
            click: true,  // 可以点击  用法:要覆盖本机滚动,BetterScroll必须禁止某些默认浏览器行为,例如鼠标单击。如果您希望您的应用程序响应click事件,则必须将该选项显式设置为true。然后BetterScroll将向其_constructed调度事件添加一个私有属性,该属性的值为true。
            mouseWheel: true,  // 鼠标滚轮
            scrollX: true,
            scrollY: false,
            scrollbar: {
                customElements: [horizontal],  // 自定义滚动条  [horizontal,vertical]  横竖 我只用了 horizontal 滚动条 DOM 传过来的 REF
                fade: true,  // 淡入淡出
                interactive: true,
                scrollbarTrackClickable: true
            }  // 滚动条
        })
        setDsContainer(ds)  // 保存 ds
    }
}


// 防抖函数
export const debounce = (func, delay) =>
{
    let timer = null;
    return function (...args)
    {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() =>
        {
            func.apply(this, args);
        }, delay);
    };
}

JS

import React, { useRef, useEffect, useState, Fragment } from 'react'
import styles from './style.less'
import { BS, DS, debounce } from './utils/index'
import { Select } from 'antd';
const { Option } = Select
const index = () =>
{
    const [rollingArea, setRollingArea] = useState(0)  // 滚动位置
    const [bsContainer, setBsContainer] = useState(null)  // 保存 BS
    const [dsContainer, setDsContainer] = useState(null)  // 保存 DS
    const [beforePullDown, setBeforePullDown] = useState(true) // 正在加载中 ~
    const [isPullingDown, setIsPullingDown] = useState(false)  // 加载完成
    const [selectName, setSelectName] = useState('yus')
    const scrollRef = useRef()  // X轴滚动
    const scrollRefX = useRef()  // Y轴滚动
    const scrollBar = useRef()  // 滚动条
    useEffect(() =>
    {  // 发送数据请求 设置订阅/启动定时器 手动更改 DOM 等 ~  // scrolled 记录滚动  pullUp 记录上拉
        BS('wrapper', setBsContainer, { payload: { params: null, callback: { scrolled, pullUp } } })  // 传递三个参数 DOM节点、用于保存 BS 对象、 payload:传递一个 params 暂时没作用 callback 用于 接收传递过来的值
        // BS(scrollRef.current)  // 发送 Ref 也可以发送 DOM 节点  BS('wrapper')

        DS({ wrapper: 'wrapperX', scrollBar: scrollBar.current }, setDsContainer)
        // DS({ wrapper: scrollRefX.current, scrollBar: scrollBar.current }, setDsContainer)
        return () =>
        {  // 组件卸载之前 做一些收尾工作 比如清楚定时器/取消订阅 等 ~
        }
    }, [])  // 检测数组内变量 如果为空 则监控全局
    // 如果有数据请求 下拉再次请求
    const requestData = () =>
    {
        console.log('请求数据了');
    }
    // 滚动事件
    const scrolled = debounce((roll, bs) =>
    {  // roll 滚动位置 bs BScroll 对象
        const { x, y } = roll  // x y 轴的距离
        // bs && bs.refresh()  //  按钮因为没有写到 content 也就不会影响到滚动的高度 所以也就不需要加 refresh
        setRollingArea(y)  // 保存 Y 轴位置
    }, 100)  // 展示的慢 因为加了防抖函数

    const pullUp = async (data, news) =>
    {
        // console.log(news, '接收你发来的消息');
        setBeforePullDown(false)  // 更换上拉 顶部展示
        setIsPullingDown(true)

        // 如果有请求数据的话 页码加1 page += 1
        await requestData()  // 因为没有发送网络请求 就包了一个 定时器 有网络请求则不需要
        setTimeout(() =>
        {
            setBeforePullDown(true)
            setIsPullingDown(false)
        }, 500)

    }
    // 点击到指定区域
    const btnClick = debounce(() =>
    {
        console.log(dsContainer, 'dsContainer');

        bsContainer.scrollTo(200, -1000, 2000)  // X轴 Y轴 时间 
        dsContainer.scrollTo(-400, 0, 2000)  // 控制 X 轴滚动

        // dsContainer.scrollBy(400, 0, 2000)  // 控制 X 轴滚动条
    }, 1000)
    // 返回顶部
    const btnBack = () =>
    {
        bsContainer.scrollTo(750, 0, 2000)  // X轴 Y轴 时间 
        dsContainer.scrollTo(0, -40, 2000)  // 控制 X 轴滚动
    }
    // Dom list
    const scrollList = () =>
    {
        return (
            <Fragment>
                <div>1</div>
                <div>2</div>
                <div>3</div>
                <div>4</div>
                <div>5</div>
                <div>6</div>
                <div>7</div>
                <div>8</div>
                <div>9</div>
                <div>10</div>
                <div>11</div>
                <div>12</div>
                <div>13</div>
                <div>14</div>
                <div>15</div>
                <div>16</div>
                <div>17</div>
                <div>18</div>
                <div>19</div>
                <div>20</div>
                <div>21</div>
                <div>22</div>
                <div>23</div>
                <div>24</div>
                <div>25</div>
                <div>26</div>
                <div>27</div>
                <div>28</div>
                <div>29</div>
                <div>30</div>
                <div>31</div>
                <div>32</div>
                <div>33</div>
                <div>34</div>
                <div>35</div>
                <div>36</div>
                <div>37</div>
                <div>38</div>
                <div>39</div>
                <div>40</div>
                <div>41</div>
                <div>42</div>
                <div>43</div>
                <div>44</div>
                <div>45</div>
                <div>46</div>
                <div>47</div>
                <div>48</div>
                <div>49</div>
                <div>50</div>
                <div>51</div>
                <div>52</div>
                <div>53</div>
                <div>54</div>
                <div>55</div>
                <div>56</div>
                <div>57</div>
                <div>58</div>
                <div>59</div>
                <div>60</div>
                <div>61</div>
                <div>62</div>
                <div>63</div>
                <div>64</div>
                <div>65</div>
                <div>66</div>
                <div>67</div>
                <div>68</div>
                <div>69</div>
                <div>70</div>
                <div>71</div>
                <div>72</div>
                <div>73</div>
                <div>74</div>
                <div>75</div>
                <div>76</div>
                <div>77</div>
                <div>78</div>
                <div>79</div>
                <div>80</div>
                <div>81</div>
                <div>82</div>
                <div>83</div>
                <div>84</div>
                <div>85</div>
                <div>86</div>
                <div>87</div>
                <div>88</div>
                <div>89</div>
                <div>90</div>
                <div>91</div>
                <div>92</div>
                <div>93</div>
                <div>94</div>
                <div>95</div>
                <div>96</div>
                <div>97</div>
                <div>98</div>
                <div>99</div>
                <div>100</div>
            </Fragment>

        )
    }
    // Dom list
    const scrollList2 = () =>
    {
        return (
            <Fragment>
                <div>1</div>
                <div>2</div>
                <div>3</div>
                <div>4</div>
                <div>5</div>
                <div>6</div>
                <div>7</div>
                <div>8</div>
                <div>9</div>
                <div>10</div>
                <div>11</div>
                <div>12</div>
                <div>13</div>
                <div>14</div>
                <div>15</div>
                <div>16</div>
                <div>17</div>
                <div>18</div>
                <div>19</div>
                <div>20</div>
                <div>21</div>
                <div>22</div>
                <div>23</div>
                <div>24</div>
                <div>25</div>
                <div>26</div>
                <div>27</div>
                <div>28</div>
                <div>29</div>
                <div>30</div>
                <div>31</div>
                <div>32</div>
                <div>33</div>
                <div>34</div>
                <div>35</div>
                <div>36</div>
                <div>37</div>
                <div>38</div>
                <div>39</div>
                <div>40</div>
                <div>41</div>
                <div>42</div>
                <div>43</div>
                <div>44</div>
                <div>45</div>
                <div>46</div>
                <div>47</div>
                <div>48</div>
                <div>49</div>
                <div>50</div>
                <div>51</div>
                <div>52</div>
                <div>53</div>
                <div>54</div>
                <div>55</div>
                <div>56</div>
                <div>57</div>
                <div>58</div>
                <div>59</div>
                <div>60</div>
                <div>61</div>
                <div>62</div>
                <div>63</div>
                <div>64</div>
                <div>65</div>
                <div>66</div>
                <div>67</div>
                <div>68</div>
                <div>69</div>
                <div>70</div>
                <div>71</div>
                <div>72</div>
                <div>73</div>
                <div>74</div>
                <div>75</div>
                <div>76</div>
                <div>77</div>
                <div>78</div>
                <div>79</div>
                <div>80</div>
                <div>81</div>
                <div>82</div>
                <div>83</div>
                <div>84</div>
                <div>85</div>
                <div>86</div>
                <div>87</div>
                <div>88</div>
                <div>89</div>
                <div>90</div>
                <div>91</div>
                <div>92</div>
                <div>93</div>
                <div>94</div>
                <div>95</div>
                <div>96</div>
                <div>97</div>
                <div>98</div>
                <div>99</div>
                <div>100</div>
                <div>101</div>
                <div>102</div>
                <div>103</div>
                <div>104</div>
                <div>105</div>
                <div>106</div>
                <div>107</div>
                <div>108</div>
                <div>109</div>
                <div>110</div>
                <div>111</div>
                <div>112</div>
                <div>113</div>
                <div>114</div>
                <div>115</div>
                <div>116</div>
                <div>117</div>
                <div>118</div>
                <div>119</div>
                <div>120</div>
                <div>121</div>
                <div>122</div>
                <div>123</div>
                <div>124</div>
                <div>125</div>
                <div>126</div>
                <div>127</div>
                <div>128</div>
                <div>129</div>
                <div>130</div>
                <div>131</div>
                <div>132</div>
                <div>133</div>
                <div>134</div>
                <div>135</div>
                <div>136</div>
                <div>137</div>
                <div>138</div>
                <div>139</div>
                <div>140</div>
                <div>141</div>
                <div>142</div>
                <div>143</div>
                <div>144</div>
                <div>145</div>
                <div>146</div>
                <div>147</div>
                <div>148</div>
                <div>149</div>
                <div>150</div>
            </Fragment>

        )
    }
    const listX = () =>
    {
        return (
            <Fragment>
                <span>羽神天下第一</span>
                <span>2</span>
                <span>3</span>
                <span>4</span>
                <span>5</span>
                <span>6</span>
                <span>7</span>
            </Fragment>
        )
    }
    const listX2 = () =>
    {
        return (
            <Fragment>
                <span>少主天下第一</span><span>2</span><span>3</span><span>4</span><span>5</span><span>6</span><span>7</span><span>8</span><span>9</span><span>10</span><span>11</span><span>12</span><span>13</span><span>14</span><span>15</span><span>16</span><span>17</span><span>18</span><span>19</span><span>20</span>
            </Fragment>
        )
    }
    // 下拉框
    const handleChange = async (value) =>  // 点击下拉框改变了容器高度 则必须要 refresh 刷新容器
    {
        await bsContainer.scrollTo(0, 0, 400)  // 会到顶部
        await dsContainer.scrollTo(0, 0, 200)
        setSelectName(value)
        await bsContainer.refresh()
        await dsContainer.refresh()
    }
    return (
        <div className={styles.container}>
            {rollingArea <= -1000 ? (<button onClick={btnBack} className={styles.btn} >点击返回顶部</button>) : null}

            <Select defaultValue={selectName} style={{ width: 120 }} onChange={handleChange}>
                <Option value="yus">羽神</Option>
                <Option value="sz">少主</Option>
            </Select>
            <div id='wrapper' ref={scrollRef} className={styles.wrapper}>
                <div className={styles.content}>
                    {beforePullDown ? <div className={styles.status}>正在加载中~</div> : null}
                    {isPullingDown ? <div className={styles.status}>数据加载成功了哦</div> : null}
                    <button onClick={btnClick}>按钮点击</button>
                    {selectName === 'yus' ? scrollList() : null}
                    {selectName === 'sz' ? scrollList2() : null}
                </div>
            </div>
            <div ref={scrollRefX} id="wrapperX" className={styles.scrollCoreX}>
                <div className={styles.scrollCoreYContent}>
                    {selectName === 'yus' ? listX() : null}
                    {selectName === 'sz' ? listX2() : null}
                </div>
                {/* 自定义滚动条 */}
                <div ref={scrollBar} className={styles.horizontal_scrollbar}>
                    <div className={styles.horizontal_indicator}></div>
                </div>
            </div>
        </div >
    )
}

export default index

CSS

.container {
  position: relative;
  width: 100%;
  height: 100%;

  .wrapper {
    position: relative;
    margin: 0 auto;
    width: 800px;
    height: 400px;
    background-color: pink;
    overflow: hidden;


  }
}

.status {
  position: absolute;
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
  transform: translateY(-100%) translateZ(0);
  text-align: center;
  color: #999;
}

.btn {
  position: fixed;
  top: 100px;
  right: 600px;
  z-index: 1;
}


// BetterScroll 实现了水平滚动,对 CSS 要求更高。首先,您需要确保包装器不包装,并且内容的显示是内联块。
/* 
.scroll-wrapper
  // ...
  white-space nowrap
.scroll-content
  // ...
  display inline-block 
*/

.scrollCoreX {
  white-space: nowrap;
  margin: 30px auto;
  width: 600px;
  height: 200px;
  background-color: yellow;
  overflow: hidden;

  .scrollCoreYContent {
    display: inline-block;

    span {
      display: inline-block;
      margin: 20px;
      width: 200px;
      height: 200px;
      background-color: burlywood;
    }

  }

  .horizontal_scrollbar {
    position: absolute;
    left: 50%;
    bottom: 10px;
    width: 100px;
    height: 7px;
    border-radius: 5px;
    transform: translateX(-50%) translateZ(0);
    background-color: rgb(200, 200, 200, 0.3);

    .horizontal_indicator {
      height: 100%;
      width: 20px;
      border-radius: 5px;
      background-color: #db8090;
    }
  }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

臧小川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值