消息通知文字竖向无缝轮播组件实现历程
背景
最近有个需求需要做一个无缝轮播的消息通知,并且需要抽离成通用组件,记录下实现这个组件的历程。
先看效果
实现过程
思考(part-1)
因为刚开始给的设计稿是没有动画效果的,我刚开始想的效果是只有红色加粗的文字轮播,其他文字不变;然后想着看下有没有已经实现好的轮子,找到一个 js 库:react-text-loop;但是经过我的考虑,如果只是部分文字滚动,红色加粗的文字可能宽度不一样,会导致其他文字换位,所以还是想着整条文字滚动会比较好。
使用 antd-m Swiper 实现(part-2)
想到这个滚动效果在移动端应该很常见,antd-m 应该可能会有组件吧,去瞧瞧👀;noticeBar 组件只有横向滚动文字,没有竖直滚动的。既然没有,那就用其他方式实现吧,最简单的就是用 Swiper 组件设置成 竖向滚动,因为我负责的项目基本都用 antd-m,所以就用 antd-m 的 Swiper 来实现了。
实现过程遇到的问题
antd-m 的 Swiper 组件竖向滚动必须指定高度
在使用 antd-m 的 Swiper 组件竖向滚动的方式好像有问题,但是看文档的使用又是正常,结果发现竖向滚动需要指定高度,所以文档还是要仔细看清楚: Swiper 竖向文档
依赖冲突问题
在自己仓库使用很正常,一点问题都没有;然后打算抽离到我们项目的组件库中,然后在把项目中使用替换成组件库的包,过程很顺畅;过了段时间另一个项目要使用我们的组件,然后我就把包发给他,结果他说他项目里用不了,会报错。
然后 clone 他的项目试了下,果然是有问题的,因为他们项目里用的是 antd-m 2.x,2.x 没有 Swiper 组件,而我的组件库依赖的是 antd-m 5.x,看了下他们仓库用的是antd-m 2.x 和 5.x 共存的方式,可以看一下这个 antd-m 迁移指南,如果要两个版本共存且之前用的是组件按需导入,那么组件按需导入的配置也会有问题,因为两个版本的文件差异比较大,所以需要改一下按需导入的配置:
module.exports = {
"plugins": [
// 原配置
// [
// 'import',
// {
// libraryName: 'antd-mobile',
// libraryDirectory: 'lib',
// style: 'css',
// },
// 'antd-mobile',
// ]
// 修改为
[
'import',
{
libraryName: 'antd-mobile',
customName: (name, file) => {
const { filename } = file.opts;
if (filename.includes('/es/')) {
return `antd-mobile/es/components/${name}`;
}
return `antd-mobile/lib/${name}`;
},
style: (name, file) => {
const { filename } = file.opts;
if (filename.includes('/es/')) {
// 如果项目已经有 global 文件,return false 即可
// 如果没有,这样配置后就不需要手动引入 global 文件
return 'antd-mobile/es/global';
}
return `${name}/style/css`;
},
},
'antd-mobile',
]
]
}
想彻底解决这个依赖冲突问题
其实修改完配置之后使用就正常了,但是我考虑到如果之后想使用这个组件,如果 antd-m 版本不是 5.x,那么有一个项目就要改一个配置,很烦人;而且 antd-m Swiper 竖向需要指定高度,如果都需要指定高度了,那么我直接实现一个滚动动画应该也很简单吧,说干就干。
自己手写一个轮播组件(pard-3)
手动实现轮播还是比较简单的,只不过无缝轮播那里需要处理下,传统的方式都是通过在轮播 item 首尾复制 item 插入,当轮播到最后一个,用复制的 item 来承接,之后在回归原位正常滚动。
手写实现思路
- 传入轮播容器的高度,使用
transform: translate3d(0px, ${容器高度}, 0px);
每次移动列表 translateY 的距离为容器的高度。 - 处理无缝轮播,因为这个组件没有手动滑动轮播,自由自动向下轮播,所以不需要考虑反方向的轮播处理;当轮播到最后一个 item,那么就将第一个 item
transform: translateY(${轮播列表高度})
,这时候第一个就在最后一个下面,监听轮播列表onTransitionEnd
,判断当前是否轮播到第一个,是的话就将轮播列表的 translateY 的距离归 0。
最终实现代码
其实最后是封装成了一个 react 组件,但是掘金上为了大家看到更好的效果,用原生的方式简单写了下。如果需要封装成 react/vue 组件参考下方代码应该就够了。
容器未 hidden 效果
组件封装的设计
这里放一下我封装组件设计的 props
interface IProps {
/** 轮播通知 list */
list: any[];
/** noticebar 显隐控制 */
visible?: boolean;
/** 单个轮播 item 的高度, 传入 750 设计稿的数字,会转成 vw */
swiperHeight?: number;
/** 每条通知轮播切换的间隔 */
interval?: number;
/** 轮播动画的持续时间 */
animationDuration?: number;
/** 是否展示关闭按钮 */
closeable?: boolean;
/** 关闭按钮点击的回调 */
onClose?: () => void;
/** 自定义轮播 item 的内容 */
renderItem?: (item: any) => React.ReactNode;
/** notice 的标题 */
noticeTitle?: ReactNode;
/** notice 右边自定义 icon */
rightIcon?: ReactNode;
/** 是否展示 notice 左边 icon */
showLeftIcon?: boolean;
/** notice 左边自定义 icon */
leftIcon?: ReactNode;
/** 自定义类名 */
className?: string;
}
来源:juejin.cn/post/7330054489079169065