说到底还是需求啦~┓( ´∀` )┏
需求:就是一个表格,可以自滚动轮播,之前做过用css实现简单的表格自滚动,但是这个表格另外的需求,鼠标hover的时候,滚动停止,鼠标移开的时候滚动继续,还要有hover状态和点击事件,感觉之前用css来做滚动已经没法满足了。(:з」∠) 所以就着手重新搞一个了。
思路:
- 因为表格头部是固定不变的,只是表格的body进行滚动,所以把表格头部和滚动的部分分为两个表格
- 利用
scrollTop
来实现滚动 - 利用
setInterval
做轮询滚动功能 - 利用
onMouseEnter
和onMouseLeave
事件来做hover监听
问题:
- 因为是两个表格,要解决不同表格里文字对齐问题
- 要解决
scrollTop
到最底部的问题 - 要解决表格滚动时候衔接问题
嘿呀,本来还想写一波问题解决思路,但还是直接上代码吧哈哈哈哈(肯定不是因为我懒了)
import React, { CSSProperties, ReactNode, useCallback, useEffect, useRef } from 'react';
import './customTable.scss';
import { useImmer } from 'use-immer';
interface PropsColumn {
dataIndex: string;
title: string;
width?: number;
render?: (data: any) => ReactNode;
}
interface Props {
uId: string;
column?: PropsColumn[];
data?: any[];
autoScroll?: boolean;
scrollSpeed?: number;
height: number;
thFont?: number;
tbFont?: number;
isRowSelect?: boolean;
rowSelectEvent?: (data: any, index: number) => void;
style?: CSSProperties;
}
const MyMar: object = {};
const speed = 50;
const CustomTable = ({
uId,
column = [],
data = [],
autoScroll = true,
scrollSpeed,
height = 0,
thFont = 16,
tbFont = 16,
isRowSelect = false,
rowSelectEvent,
style,
}: Props) => {
const tableRef = useRef<HTMLDivElement>(null);
const tableRef1 = useRef<any>(null);
const tableRef2 = useRef<HTMLDivElement>(null);
const [state, setState] = useImmer({
columnWidth: [] as number[],
selected: '',
tableData: data.map((item) => {
return { rowSelect: false, ...item };
}),
} as any);
const Marquee = () => {
if (tableRef2.current && tableRef.current && tableRef1.current) {
// if (tableRef2.current.offsetTop - tableRef.current.scrollTop <= 0) {
if (tableRef2.current.scrollHeight - tableRef.current.scrollTop <= 0) {
tableRef.current.scrollTop -= tableRef1.current.offsetHeight;
} else {
tableRef.current.scrollTop += 1;
}
}
};
const handleOnMouseOver = () => {
clearInterval(MyMar[uId]);
MyMar[uId] = undefined;
};
const handleOnMouseOut = () => {
if (!MyMar[uId] && autoScroll) MyMar[uId] = setInterval(Marquee, scrollSpeed || speed);
};
const onclickEvent = (value: any, index: number) => {
if (isRowSelect) {
setState((pre) => {
if (pre.selected !== '') {
pre.tableData[state.selected].rowSelect = undefined;
}
pre.tableData[index].rowSelect = true;
pre.selected = index;
});
}
if (isRowSelect && rowSelectEvent) {
const item = { ...value };
delete item.rowSelect;
rowSelectEvent(item, index);
}
};
// 处理表格头部
const handleTHeader = useCallback(() => {
setTimeout(() => {
if (
tableRef1.current &&
tableRef1.current.children[0] &&
tableRef1.current.children[0].rows[0] &&
tableRef1.current.children[0].rows[0].cells
) {
let width = [] as number[];
const elements: any = Array.from(tableRef1.current.children[0].rows[0].cells);
width = elements.map((item) => {
return item.offsetWidth;
});
setState((pre) => {
pre.columnWidth = width;
});
}
}, 800);
}, []);
useEffect(() => {
if (autoScroll) {
if (tableRef2.current) {
tableRef2.current.innerHTML = '';
}
if (tableRef.current && tableRef1.current) {
tableRef.current.scrollTop -= tableRef1.current.offsetHeight;
}
handleOnMouseOver();
setTimeout(() => {
if (
tableRef2.current &&
tableRef1.current &&
tableRef.current &&
tableRef.current.scrollHeight > tableRef.current.clientHeight
) {
tableRef2.current.innerHTML = tableRef1.current.innerHTML;
}
}, 300);
setTimeout(() => {
handleOnMouseOut();
}, 1000);
}
setState((pre) => {
pre.tableData = data.map((item) => {
return { rowSelect: false, ...item };
});
});
}, [JSON.stringify(data)]);
useEffect(() => {
if (autoScroll) {
setTimeout(() => {
if (
tableRef2.current &&
tableRef1.current &&
tableRef.current &&
tableRef.current.scrollHeight > tableRef.current.clientHeight
) {
tableRef2.current.innerHTML = tableRef1.current.innerHTML;
if (!MyMar[uId]) MyMar[uId] = setInterval(Marquee, scrollSpeed || speed);
}
}, 2500);
}
handleTHeader();
return handleOnMouseOver;
}, [handleTHeader, uId, autoScroll]);
return (
<div className="jsc-scroll-table" style={{ height, ...style }}>
<table cellSpacing="0" cellPadding="0">
<thead>
<tr>
{column.map((item, index) => {
return (
<td
key={item.title}
style={{
width: item.width || state.columnWidth[index],
fontSize: `${thFont}px`,
}}
>
{item.title}
</td>
);
})}
</tr>
</thead>
</table>
<div
ref={tableRef}
id={`${uId}-table1`}
style={{
overflowY: autoScroll ? 'hidden' : 'scroll',
height: height ? height - 40 : '100%',
}}
onMouseEnter={handleOnMouseOver}
onMouseLeave={handleOnMouseOut}
onFocus={() => 0}
onBlur={() => 0}
>
<div id={`${uId}-tbody1`} ref={tableRef1}>
<table cellSpacing="0" cellPadding="0">
<tbody>
{state.tableData.map((value, index) => {
return (
<tr
key={column ? `tr${column[value]}${index}` : `tr${index}`}
className={value.rowSelect ? 'row-selected' : ''}
style={isRowSelect ? { cursor: 'pointer' } : { cursor: 'default' }}
onClick={() => {
onclickEvent(value, index);
}}
>
{column.map((col) => {
if (value[col.dataIndex]) {
return col.render ? (
<td
key={`td-${col.dataIndex}-${value[col.dataIndex]}-${index}`}
style={{ width: col.width, fontSize: `${tbFont}px` }}
>
{col.render(value)}
</td>
) : (
<td
key={`td-${col.dataIndex}-${value[col.dataIndex]}-${index}`}
style={{ width: col.width, fontSize: `${tbFont}px` }}
>
{value[col.dataIndex]}
</td>
);
}
return col.render ? (
<td
key={`td-${col.dataIndex}-${index}`}
style={{ width: col.width, fontSize: `${tbFont}px` }}
>
{col.render(value)}
</td>
) : (
<td style={{ width: col.width, fontSize: `${tbFont}px` }} key={index} />
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
<div id={`${uId}-tbody2`} ref={tableRef2} />
</div>
</div>
);
};
export default React.memo(CustomTable);