原理
从上面的图中我们可以看到当list的宽度<wrap的宽度(containerWidth)时,会出现滚动后出现空白的情况。那么第二张图,list的宽度>=wrap的两倍,就能在向左滚动完list的一半后,不会出现空白,而且为了给人一种无限滚动的效果,list的前后两部分数据要保持一致。 所以滚动的距离 = 展示数据的个数 * 每项的宽度,而为了无限滚动效果,我们还需要对原始数据进行处理。 分为以下几种情况:
- 数据个数>= 一屏展示个数(showNum)
此时重复两次原始数据就能得到滚动数据
- 数据个数< 一屏展示个数
首先我们要保证没有空白,那要如何填充呢?只填充到=showNum,行不行呢? 我们可以看一下: 比如说原始数据为[1,2,3],填充完再进行重复则为 [1,2,3,1,1,2,3,1],这样会出现1这一项连续出现了。 所以最好的方式是直接填充原始数据直到>=showNum,所以最终我们得到的滚动数据是[1,2,3,1,2,3 ,1,2,3,1,2,3] 提示:屏幕的宽度==每个数据的宽度 * 显示的条数
javascript
import { useEffect } from 'react';
import * as React from 'react';
import './index.less';
import { InfoType } from '@/api/home';
interface Props {
showNum: number;
speed: number;
containerWidth?: number;
containerHeight: number;
data: InfoType[];
hoverStop?: boolean;
direction?: 'left' | 'right';
children?: React.ReactNode;
}
function AutoplayCarousel({
showNum,
speed,
containerWidth,
containerHeight,
data,
hoverStop = false,
direction = 'left',
children,
}: Props) {
const container = React.useRef<HTMLDivElement>(null);
const length = data.length * 2;
const itemHeight = containerHeight / showNum;
useEffect(() => {
// 创建一个新的样式表对象
const style = document.createElement('style');
// 定义样式表的内容
let start = '0';
let end = `-${(itemHeight * length) / 2}`;
if (direction === 'right') {
start = end;
end = '0';
}
style.innerText = `
@keyframes templates-partner-moving {
0% {
transform: translateY(${start}px);
}
100% {
transform: translateY(${end}px);
}
}
`;
if (hoverStop) {
style.innerText += `.list:hover {
/*鼠标经过后,动画暂停*/
animation-play-state: paused !important;
}`;
}
// 将样式表插入到文档头部
document.head.appendChild(style);
// 组件卸载时清除样式表
return () => document.head.removeChild(style) as any;
}, []);
// ---------------------start 鼠标移入停止滚动 ---------------------
function scrollAnimation(state: string) {
//获取children
const domList = container.current?.children;
if (!domList?.length) return;
for (let i = 0; i < domList.length; i += 1) {
//修改children的样式
(domList[i] as HTMLElement).style.animationPlayState = state;
}
}
useEffect(() => {
const paused = () => scrollAnimation('paused');
const running = () => scrollAnimation('running');
container.current?.addEventListener('mouseenter', paused);
container.current?.addEventListener('mouseleave', running);
return () => {
container.current?.removeEventListener('mouseenter', paused);
container.current?.removeEventListener('mouseleave', running);
};
}, []);
return (
<div
ref={container}
style={{ width: 400, height: containerHeight }}
className="wrap"
>
<div
className="list"
style={{
animation: `templates-partner-moving ${(length / showNum / 2) * speed}s infinite linear`,
}}
>
{children}
{children}
</div>
</div>
);
}
export default AutoplayCarousel;css
javascript
.wrap {
overflow: hidden;
.list {
position: relative;
top: 0px;
left: 0px;
height: 100%;
// display: flex;
}
}使用
javascript
<AutoplayCarousel
containerHeight={123.09}
showNum={3}
speed={5}
data={data}
>
{data.map(({ fileType, title, titleEn, riBusinessAttachmentsDTO }, index) => {
return (
<div
className="scrollItem"
key={index}
>
<div className="type">
<img src={require(`@/assets/images/${returnTypeImg(fileType)}.png`)} />
<span>{returnType(fileType)}</span>
</div>
<div className="title">
<MyTooltip
onDownload={() => {filePreview(riBusinessAttachmentsDTO)}}
text={lang === 'en' ? titleEn : title}
width={300}
></MyTooltip>
</div>
</div>
);
})}
</AutoplayCarousel>