使用的是我之前开发的开源库 bear-react-carousel
主要试试看能做到什么程度,之前公司也有类似的需求
废话不多说,直接开始
需求
首先我们先看掘金在APP端的预览结果
- 可滑动的部分只有 卡片的部分
- 并且在滑动时等级卡片会往上移动
- 等级卡片显示为置中为 选中项目
- 等级卡片下方需要被遮住(圆弧),不显示完全
- 等级名称项目需同步移动
- 等级名称项目移动时圆弧移动
- 等级名称线需同步移动
- API 捞回时,预设需要移动到选中的等级(无动画移动)
开发轮播项目
轮播项目可以切分为:
- 等级卡片,显示1.2个并且置
tsx
<BearCarousel
slidesPerView={1.2}
spaceBetween={20}
isCenteredSlides={true}
// ...ignore
/>
- 等级名称
- 等级线 这两个都是显示3个并且置中
tsx
<BearCarousel
slidesPerView={3}
isCenteredSlides={true}
// ...ignore
/>
所以开发目标是 移动等级卡片
时,同步移动 等级名称
与等级线
tsx
import React, {useCallback, useEffect, useRef, useState} from 'react';
import BearCarousel, {TMoveEffectFn, TBearSlideItemDataList, BearSlideCard, elClassName, Controller, TOnSlideChange} from 'bear-react-carousel';
const MemberLevelWrapper = () => {
const carouselMainRef = useRef<BearCarousel>(null);
const carouselMetaRef = useRef<BearCarousel>(null);
const carouselLineRef = useRef<BearCarousel>(null);
return <>
{/* 等级卡片 */}
<BearCarousel
ref={carouselMainRef}
syncCarouselRefs={[carouselMetaRef, carouselLineRef]}
slidesPerView={1.2}
spaceBetween={20}
isCenteredSlides={true}
// ...ignore
/>
{/* 等级名称 */}
<BearCarousel
ref={carouselMetaRef}
// ...ignore
/>
{/* 等级名称线 */}
<BearCarousel
ref={carouselLineRef}
// ...ignore
/>
</>;
};
然后再来是 滑动时等级卡片会往上移动, 新增一个 动画 effect function
tsx
import BearCarousel, {TMoveEffectFn} from 'bear-react-carousel';
const mainMoveEffectFn: TMoveEffectFn = useCallback((percentageInfo) => {
const transformY = 40;
return {
transform: `translate(0px, ${-transformY * (percentageInfo.calcPercentage - 1)}px)`,
};
}, []);
<BearCarousel
ref={carouselMainRef}
syncCarouselRefs={[carouselMetaRef, carouselLineRef]}
moveEffect={{
moveFn: mainMoveEffectFn,
}}
// ...ignore
/>
等级名称的圆弧移动
tsx
const levelNameMoveEffectFn: TMoveEffectFn = useCallback((percentageInfo) => {
const transformY = -19;
return {
transform: `translate(0px, ${-transformY * (percentageInfo.calcPercentage - 1)}px)`,
};
}, []);
<BearCarousel
ref={carouselMainRef}
moveEffect={{
moveFn: levelNameMoveEffectFn,
}}
// ...ignore
/>
至于圆弧的部分,我们最后使用遮罩的方式来显示,并且关闭 NavButton、Pagination、MouseMove,画SVG不熟的话,可以使用这个工具 yqnn.github.io/svg-path-ed...
tsx
<LevelLine>
<LineBearCarousel
ref={carouselLineRef}
data={lineData}
slidesPerView={3}
isCenteredSlides={true}
isEnableNavButton={false}
isEnablePagination={false}
isEnableMouseMove={false}
/>
<svg height="100%" width="100%">
<clipPath id="wave12">
{/*跟隨線*/}
<path d="M 0 4 C 175 30 175 30 356 4 L 356 2 C 175 28 175 28 0 2" stroke="black" fill="transparent"/>
</clipPath>
</svg>
</LevelLine>
而 Level Card 遮罩的部分一样,但我用了3块
tsx
<svg height="100%" width="100%">
<clipPath id="wave10">
<path d="M 0,0 356,0 356,130 0,130" stroke="black" fill="transparent"/>
{/* 圓弧 */}
<path d="M 0 130 C 175 155 175 155 356 130" stroke="black" fill="transparent"/>
{/* 下箭頭 */}
<path d="M 152 143 L 176 153 L 178 153 L 202 143" stroke="black" fill="transparent"/>
</clipPath>
</svg>
最后就是 预设选中等级的部分(无动画方式移动)
tsx
const [carouselMainController, setMainController] = useState<Controller>();
const [currLevel, setCurrLevel] = useState<{lv: number,count: number}|undefined>();
useEffect(() => {
carouselMainController?.slideToPage(5, false);
}, [carouselMainController]);
<BearCarousel
ref={carouselMainRef}
syncCarouselRefs={[carouselMetaRef]}
onSlideChange={handleSlideChange}
setController={setMainController}
slidesPerView={1.2}
spaceBetween={20}
isCenteredSlides={true}
// ...ignore
/>
完成
目前成果作为文件的展示使用,想试试看的客观可以实际操作看看
bear-react-carousel.pages.dev/
以上就完成了,基本上如果两边都需要移动的话,互相同步在bear-react-carousel
因为是进行独立的同步控制,所以不会循环互控,但也因为这样所以目前是无法A控制B , B自动再控制C,所以在这边可以发现是A控制B和C。