封装组件:
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>
)
})