web-backset.cn/apps/web-main/src/view/Course/components/Timeline/index.tsx
2023-03-02 11:30:38 +08:00

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;