160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
import dayjs from "dayjs";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import "./index.less";
|
|
|
|
export interface IOnScrollParam {
|
|
top: number;
|
|
height: number;
|
|
}
|
|
|
|
export interface IModel {
|
|
top: number;
|
|
}
|
|
|
|
interface IProps {
|
|
className: string;
|
|
data: any;
|
|
onScroll?: (p: IOnScrollParam) => void;
|
|
model?: IModel;
|
|
}
|
|
|
|
/**
|
|
* 处理年份在时间轴上面的显示
|
|
*/
|
|
const filterYearOnce = (data: any[]) => {
|
|
const flag: any[] = [];
|
|
return data.map((item) => {
|
|
if (!flag.includes(item.year)) {
|
|
flag.push(item.year);
|
|
return { ...item, visible: true };
|
|
} else {
|
|
return { ...item, visible: false };
|
|
}
|
|
});
|
|
};
|
|
|
|
function Timeline(props: IProps) {
|
|
const [cursorStatic, setCursorStatic] = useState({
|
|
top: -4,
|
|
color: "var(--color-fill-4)",
|
|
});
|
|
const [cursorActive, setCursorActive] = useState({
|
|
top: 0,
|
|
color: "var(--color-border-3)",
|
|
});
|
|
const [intervalPixel, setIntervalPixel] = useState<number>(1);
|
|
const [timelineData, setTimelineData] = useState(filterYearOnce(props.data));
|
|
const cursorActiveRef = useRef<HTMLDivElement | null>(null);
|
|
const cursorStaticRef = useRef<HTMLDivElement | null>(null);
|
|
const orbitRef = useRef<HTMLDivElement>();
|
|
|
|
/**
|
|
* 点击打圈圈 cursorStatic
|
|
*/
|
|
const onMouseDown = (ev: any) => {
|
|
const orbitClient = orbitRef.current!.getBoundingClientRect();
|
|
let mouseY = (ev || window.event).clientY; //鼠标按下的位置
|
|
if (mouseY > orbitClient.top && mouseY < orbitClient.bottom)
|
|
setCursorStatic((p) => ({ ...p, top: mouseY - orbitClient.top - 4 }));
|
|
};
|
|
|
|
/**
|
|
* 移动圈圈 coursorActive
|
|
*/
|
|
const onMouseMove = (ev: any) => {
|
|
const orbitClient = orbitRef.current!.getBoundingClientRect();
|
|
const mouseY = (ev || window.event).clientY;
|
|
if (mouseY > orbitClient.top && mouseY < orbitClient.bottom)
|
|
setCursorActive((p) => ({ ...p, top: mouseY - orbitClient.top - 4 }));
|
|
};
|
|
|
|
useEffect(() => {
|
|
setCursorStatic((p) => ({ ...p, top: props.model!.top }));
|
|
}, [props.model]);
|
|
|
|
useEffect(() => {
|
|
if (props.onScroll && orbitRef.current)
|
|
props.onScroll({
|
|
top: cursorStatic.top,
|
|
height: orbitRef.current.clientHeight,
|
|
});
|
|
}, [cursorStatic.top]);
|
|
|
|
useEffect(() => {
|
|
if (props.data && orbitRef.current) {
|
|
setTimelineData(filterYearOnce(props.data));
|
|
// 全部的月份数数量平均划分
|
|
const avg = orbitRef.current.clientHeight / props.data.length;
|
|
setIntervalPixel(avg);
|
|
}
|
|
}, [props.data]);
|
|
|
|
useEffect(() => {
|
|
window.addEventListener("resize", () => {
|
|
if (props.data && orbitRef.current) {
|
|
setTimelineData(filterYearOnce(props.data));
|
|
// 全部的月份数数量平均划分
|
|
const avg = orbitRef.current.clientHeight / props.data.length;
|
|
setIntervalPixel(avg);
|
|
}
|
|
});
|
|
return () => window.removeEventListener("resize", () => {});
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
className={`timeline ${props.className}`}
|
|
onMouseMove={onMouseMove}
|
|
onMouseDown={onMouseDown}
|
|
>
|
|
<svg
|
|
className="caret up"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 32 32"
|
|
>
|
|
<path d="M8 20l8-10l8 10z" fill="currentColor"></path>
|
|
</svg>
|
|
<div className="orbit" ref={orbitRef}>
|
|
<span
|
|
ref={cursorStaticRef}
|
|
className="cursor static"
|
|
style={{ ...cursorStatic }}
|
|
></span>
|
|
<span
|
|
ref={cursorActiveRef}
|
|
className="cursor active"
|
|
style={{ ...cursorActive }}
|
|
></span>
|
|
{timelineData.map((item: any, index: number) => {
|
|
return item.visible ? (
|
|
<div
|
|
key={index}
|
|
className="node bingo"
|
|
style={{ top: intervalPixel * index + "px" }}
|
|
data-year={item.year}
|
|
data-month={item.month}
|
|
></div>
|
|
) : (
|
|
<div
|
|
key={index}
|
|
className="node empty"
|
|
data-year={item.year}
|
|
data-month={item.month}
|
|
style={{ top: intervalPixel * index + "px" }}
|
|
></div>
|
|
);
|
|
})}
|
|
</div>
|
|
<svg
|
|
className="caret down"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 32 32"
|
|
>
|
|
<path d="M24 12l-8 10l-8-10z" fill="currentColor"></path>
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default Timeline;
|