react hooks结合betterscroll的运用

1,296 阅读2分钟

封装组件:

import PropTypes from "prop-types"
import BScroll from "better-scroll"
import styled from 'styled-components';

const ScrollContainer = styled.div`
  width: 100%;
  height: 100%;
  overflow: hidden;
`
function debounce(func, wait) {
    let timer;
    return function () {
        let context = this; // 注意 this 指向
        let args = arguments; // arguments中存着e

        if (timer) clearTimeout(timer);

        timer = setTimeout(() => {
            func.apply(this, args)
        }, wait)
    }
}
const Scroll = forwardRef((props, ref) => {
    const [bScroll, setBScroll] = useState();

    const scrollContaninerRef = useRef();

    const { direction, click, refresh, pullUpLoading, pullDownLoading, bounceTop, bounceBottom, Time } = props;

    const { pullUp, pullDown, onScroll } = props;

    let pullUpDebounce = useMemo(() => {
        return debounce(pullUp, 500)
    }, [pullUp]);

    let pullDownDebounce = useMemo(() => {
        return debounce(pullDown, 500)
    }, [pullDown]);

    useEffect(() => {
        const scroll = new BScroll(scrollContaninerRef.current, {
            scrollX: direction === "horizontal",
            scrollY: direction === "vertical",
            probeType: 3,
            click: click,
            bounce: {
                top: bounceTop,
                bottom: bounceBottom
            }
        });
        setBScroll(scroll);
        return () => {
            setBScroll(null);
        }
    }, []);

    useEffect(() => {
        if (!bScroll || !onScroll) return;
        bScroll.on('scroll', onScroll)
        return () => {
            bScroll.off('scroll', onScroll);
        }
    }, [onScroll, bScroll]);

    useEffect(() => {
        if (!bScroll || !pullUp) return;
        const handlePullUp = () => {
            //判断是否滑动到了底部
            if (bScroll.y <= bScroll.maxScrollY + 100) {
                pullUpDebounce();
            }
        };
        bScroll.on('scrollEnd', handlePullUp);
        return () => {
            bScroll.off('scrollEnd', handlePullUp);
        }
    }, [pullUp, pullUpDebounce, bScroll]);

    useEffect(() => {
        if (!bScroll || !pullDown) return;
        const handlePullDown = (pos) => {
            //判断用户的下拉动作
            if (pos.y > 50) {
                pullDownDebounce();
            }
        };
        bScroll.on('touchEnd', handlePullDown);
        return () => {
            bScroll.off('touchEnd', handlePullDown);
        }
    }, [pullDown, pullDownDebounce, bScroll]);

    //渲染及更新都要调用
    useEffect(() => {
        if (refresh && bScroll) {
            bScroll.refresh();
        }
    });

    useImperativeHandle(ref, () => ({
        refresh() {
            if (bScroll) {
                bScroll.refresh();
                bScroll.scrollTo(0, 0, Time);
            }
        },
        getBScroll() {
            if (bScroll) {
                return bScroll;
            }
        }
    }));

    const PullUpdisplayStyle = pullUpLoading ? { display: "" } : { display: "none" };
    const PullDowndisplayStyle = pullDownLoading ? { display: "" } : { display: "none" };
    return (
        <ScrollContainer ref={scrollContaninerRef}>
            <div>
                {props.children}
                {/* 滑到底部加载动画 */}
                <div className="pull_up_loading" style={PullUpdisplayStyle}>
                    {/* <Loading></Loading> */}
                    到底部了
                </div>
                {/* 顶部下拉刷新动画 */}
                <div className="pull_down_loading" style={PullDowndisplayStyle}>
                    {/* <Loading2></Loading2> */}
                    到顶部了
                </div>
            </div>
        </ScrollContainer>
    );
})

Scroll.defaultProps = {
    direction: "vertical",
    click: true,
    refresh: true,
    onScroll: null,
    pullUpLoading: false,
    pullDownLoading: false,
    pullUp: null,
    pullDown: null,
    bounceTop: true,
    bounceBottom: true,
    Time: 300 //返回顶部所需时间
};

Scroll.propTypes = {
    direction: PropTypes.oneOf(['vertical', 'horizontal']),
    refresh: PropTypes.bool,
    onScroll: PropTypes.func,
    pullUp: PropTypes.func,
    pullDown: PropTypes.func,
    pullUpLoading: PropTypes.bool,
    pullDownLoading: PropTypes.bool,
    Time: PropTypes.number,
    bounceTop: PropTypes.bool,//是否支持向上吸顶
    bounceBottom: PropTypes.bool//是否支持向下吸顶
};
export default memo(Scroll)

使用scroll组件:

import { Link, useDispatch, useSelector, KeepAlive } from 'umi'
import { Carousel, Tabs, } from 'antd-mobile';
import { DivWrap, TabsWrap } from './style'
import Scroll from '@/components/Scroll'


export default memo(function Home(props) {
    const dispatch = useDispatch()
    const { slides = [], sales = { goods: [] }, recommend = { goods: [] }, news = { goods: [] }, } = useSelector(state => ({
        slides: state.home.slides,
        sales: state.home.sales,
        recommend: state.home.recommend,
        news: state.home.news,
    }))
    const [CaroseHeight, setCaroseHeight] = useState(0)
    const [salesHeight, setsalesHeight] = useState(0)
    const CarouselImg = useRef(true)
    const salesRef = useRef(true)
    const [isdisplay, setdIsdisplay] = useState(false)
    const [isUpDisplay, setisUpDisplay] = useState(false)
    const [tabsCurrentPage, setTabsCurrentPage] = useState(0)
    const [currentTitle, setcurrentTitle] = useState('sales')
    const ScrollRef = useRef();
    // 请求数据
    useEffect(() => {
        //获取数据
        if (dispatch && sales.goods.length === 0 && recommend.goods.length === 0 && news.goods.length === 0) {

            dispatch({
                type: 'home/getSwiper',

            });
            dispatch({
                type: 'home/getSales',
            });
            dispatch({
                type: 'home/getRecommend',

            });
            dispatch({
                type: 'home/getNews',

            });
        }
    }, [])
    //向上加载更多数据
    const pullUp = () => {
        if (currentTitle === 'sales') {
            dispatch({
                type: 'home/getNextData',
                payload: { page: sales.page + 1, title: currentTitle },
            });
        }
        if (currentTitle === 'recommend') {
            dispatch({
                type: 'home/getNextData',
                payload: { page: recommend.page + 1, title: currentTitle },
            });
        }
        if (currentTitle === 'news') {
            dispatch({
                type: 'home/getNextData',
                payload: { page: news.page + 1, title: currentTitle },
            });
        }
    }
    //监听滚动
    const onScroll = (pos) => {
        const allHeight = CaroseHeight + salesHeight
        if (Math.abs(pos.y) > allHeight) {
            setdIsdisplay(true)
        }
        if (Math.abs(pos.y) < allHeight) {
            setdIsdisplay(false)
        }
        if (Math.abs(pos.y) > 400) {
            setisUpDisplay(true)
        }
        if (Math.abs(pos.y) === 0) {
            setisUpDisplay(false)
        }
    }
    const tabs = [
        { title: '畅销' },
        { title: '甄选' },
        { title: '最新' },
    ];
    //tabs切换
    const tabsOnchange = (tab, index) => {
        const tabs = [
            { title: '畅销', key: 'sales' },
            { title: '甄选', key: 'recommend' },
            { title: '最新', key: 'news' },
        ];
        const title = tabs[index].key
        setcurrentTitle(title)
        setTabsCurrentPage(index)
    }
    //回到顶部
    const scrollUpTo = () => {
        ScrollRef.current.refresh()
    }
    return (

        <TabsWrap isUpDisplay={isUpDisplay} isdisplay={isdisplay}>
            <div className="tabsFiex">
                <Tabs
                    tabs={tabs}
                    onChange={(tab, index) => tabsOnchange(tab, index)}
                    initialPage={2}
                    page={tabsCurrentPage}
                    swipeable={true}
                    tabBarUnderlineStyle={{ borderColor: '#ffad60' }}
                    tabBarBackgroundColor='#ceefe4'
                    tabBarActiveTextColor='#ffad60'
                ></Tabs>
            </div>
            <Scroll
                Time='800'
                ref={ScrollRef}
                onScroll={onScroll}
                pullUp={pullUp}>
                {/* //轮播图 */}
                <Carousel
                    className='swiperRef'
                    autoplay={false}
                    infinite
                >
                    {slides.map(val => (
                        <a
                            key={val}
                            href="#/"
                            style={{ display: 'inline-block', width: '100%', height: '175px' }}
                        >
                            <img
                                src={val.img_url}
                                alt=""
                                style={{ width: '100%', verticalAlign: 'top' }}
                                //监听图片是否加载完成,然后设置高度
                                onLoad={() => {
                                    //用ref保存,进行节流操作
                                    if (CarouselImg.current) {
                                        const swiperHeight = document.getElementsByClassName('swiperRef')[0].getBoundingClientRect().height
                                        setCaroseHeight(swiperHeight)
                                        CarouselImg.current = false
                                    }

                                }}
                            />
                        </a>
                    ))}
                </Carousel>
                {/* 图书推荐 */}
                <DivWrap className='salesRef'>
                    <Scroll direction='horizontal'>
                        {
                            sales.goods.map((item, index) => {
                                return (
                                    <div className='goods_item' key={index} >
                                        <Link to={`/detail/${item.id}`}>
                                            <img
                                                style={{ width: '35vw' }}
                                                src={item.cover_url}
                                                //监听图片是否加载完成,然后设置高度
                                                onLoad={() => {
                                                    //用ref保存,进行节流操作
                                                    if (salesRef.current) {
                                                        const salesHeight = document.getElementsByClassName('salesRef')[0].getBoundingClientRect().height
                                                        setsalesHeight(salesHeight)
                                                        salesRef.current = false
                                                    }

                                                }}>
                                            </img>
                                            <div>{item.title}</div>
                                        </Link>
                                    </div>
                                )
                            })
                        }

                    </Scroll>

                </DivWrap>
                {/* 选项卡 */}
                <Tabs
                    tabs={tabs}
                    onChange={(tab, index) => tabsOnchange(tab, index)}
                    initialPage={0}
                    page={tabsCurrentPage}
                    swipeable={true}
                    tabBarUnderlineStyle={{ borderColor: '#ffad60' }}
                    tabBarBackgroundColor='#ceefe4'
                    tabBarActiveTextColor='#ffad60'
                >
                    <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-around', paddingBottom: '80px' }} >
                        {
                            sales.goods.map((item, index) => {
                                return (
                                    <div key={index} style={{ textAlign: 'center', width: '45vw', overflow: 'hidden' }}>
                                        <Link to={`/detail/${item.id}`}>
                                            <img style={{ width: '45vw' }} src={item.cover_url}></img>
                                            <div style={{ color: 'black' }}>{item.title}</div>
                                        </Link>
                                    </div>
                                )
                            })
                        }
                    </div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-around', paddingBottom: '80px' }} >
                        {
                            recommend.goods.map((item, index) => {
                                return (
                                    <div key={index} style={{ textAlign: 'center', width: '45vw', overflow: 'hidden' }}>
                                        <Link to={`/detail/${item.id}`}>
                                            <img style={{ width: '45vw' }} src={item.cover_url}></img>
                                            <div style={{ color: 'black' }}>{item.title}</div>
                                        </Link>
                                    </div>
                                )
                            })
                        }
                    </div>
                    <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'space-around', paddingBottom: '80px' }} >
                        {
                            news.goods.map((item, index) => {
                                return (
                                    <div key={index} style={{ textAlign: 'center', width: '45vw', overflow: 'hidden' }}>
                                        <Link to={`/detail/${item.id}`}>
                                            <img style={{ width: '45vw' }} src={item.cover_url}></img>
                                            <div style={{ color: 'black' }}>{item.title}</div>
                                        </Link>
                                    </div>
                                )
                            })
                        }
                    </div>
                </Tabs>
            </Scroll>
            //回到顶部按钮
            <div className="UpDisplay" onClick={scrollUpTo}
                style={{
                    width: 45,
                    height: 45,
                    lineHeight: '45px',
                    textAlign: 'center',
                    backgroundColor: '#fff',
                    color: '#999',
                    fontSize: 20,
                    borderRadius: 30,
                    boxShadow: '0 2px 10px 0 rgba(0, 0, 0, 0.2)',
                    cursor: 'pointer',
                    position: 'fixed',
                    right: '30px',
                    bottom: '90px'
                }}
            >
                Up
            </div>
        </TabsWrap>

    )
})