需求:写一个统计图如下
要求:点击图例,对应的数据可以消失,Y轴是数据中的日期,X轴是一天24小时的时间段,数据为7天/30天/90天,要求统计图可以滚动,X轴不会跟着滚走。
分析:
- 统计图可以滚动,那么就是 Chart 高度不固定,是动态的,我将7天数据高度算好以后求出来的 单个高度✖️数据 ,在外围整了个7天的高度限制,然后超出数据高度就开始滚动
<div style={{ overflow: 'auto', height: 190 }}><Chart height={23 * datas.length}>.....<Chart></div>
- X轴需要手写定位在这个位置,我是直接按照X轴的位置写的,把样式写好以后,将原有的毙掉<Axis name="timeRange" visible={false} />
- 点击对应图例消失,这就是直接将图例的这个类型的visible变成false,然后将总数据里把这个所有类型里visible是true的筛出来。
<Chart height={23 * datas.length} padding={['auto', 'auto']} data={datas}>// 就是这个东西
X轴一天24小时,还要和后端数据返回的时间段对应,那么可以把x轴的时间按照一天的分钟数去计算。
<Chart height={23 * datas.length} padding={['auto', 'auto']} data={datas} forceFit scale={{ timeRange: { max: 1440,// 24:00的分钟数是最大值 tickInterval: 120,// 间隔两小时展示一次 min: 0,// 00:00的分钟数是最小值 }, }} >
数据的处理,先需要知道自己需要什么样子的数据。那么现在知道的就是,hover需要的值,x、y轴的值都需要,以及每条数据是什么图例的类型,这几个是必须值,是需要将所有数据处理为需要的字段的数组
后端返回的数据结构是:
data:{
date: string; //日期,形如:2024-01-01
useData: {
duration: number; // 使用时长,单位:min
startTime: string; // 开始时间,形如:2024-01-01 12:00:00
endTime: string; // 结束时间,形如:2024-01-01 13:00:00
}[];
}[];
分析:
可以看到 data中的所有数据结构,都是按天(date)返回的,一天中又有多个数据( useData[ ] ),我们分析了所有数据要一一展示,那么我们就需要循环useData,然后将需要的值push到一个新数组,期间也给我们也要通过计算将每个数据的类型也放到新的数据组中。
data.push({ timeString,// hover 里面需要展示的时间段 date: G.moment(item.date).format('MM-DD'),//y轴 日期 status,// 图例状态类型 percent: percent,// hover 里面需要展示的时间段 timeRange: [startMins, endMins],// x轴对应的数据段,[0,20] 表示[00:00-00:20],这个再配合我们对scale的定义就可以让我们的时间段展示在统计图了 }); // mins的计算方法 // 计算分钟数 export const calculateMinutes = (value) => { const time = moment(value, 'HH:mm');// 这里根据需求写,我这里是算当天当前时间是多少分钟,所以我的value值就是'12:00'这种格式 // 计算从当天开始到 time 的毫秒数 const milliseconds = time.diff(moment().startOf('day')); // 将毫秒数转换为分钟 const minutes = milliseconds / 60000; return minutes; };
处理完以后的数据结构应该是{xx:xx,xx:xx,...}[]
按照需求将24小时的时间段获取到,写自定义X轴用
// 获取 00:00-24:00的时间段数组['00:00','02:00',....,'24:00']
export const generateHourlyArrayWithMoment = () => {
const hourlyArray = [];
let hour = moment().startOf('day').hour(0);
for (let i = 0; i <= 12; i++) {
const formattedHour = hour.format('HH:mm');
hourlyArray.push(formattedHour);
hour.add(2, 'hours');
}
hourlyArray[hourlyArray.length - 1] = '24:00';
return hourlyArray;
};
// x 轴的展示,写法提示
<div className={styles.xAxis}>
{xAxis.map((item, index) => {
const interval = 608 / 11; // 根据原X轴计算的间隔
return (
<div className={styles.xAxisline} style={{ left: interval * (index + 1) }}>
<span className={styles.xAxisNumber}>{item}</span>
</div>
);
})}
</div>
styles={ // 具体样子自己调整,我这个是不完整的样式
.xAxis {
position: relative;
left: -14px;
}
.xAxisline {
position: absolute;
display: inline-block;
}
.xAxisNumber {
position: relative;
}}
写的时候遇到一个问题就是同一天的hover只显示第一个的真实数据,经过好几个小时尝试,发小只要在gemo中加tooltip参数即可让让每天都多条数据hover正常显示对应数据
<Geom type="interval" position="date*timeRange" color={['status', (item) => colorList[item]?.color || '#fff']} // 我的颜色是根据我处理好的每条数据对应的图例类型进行书写的,当没有一条数据的时候会报错,必须给他一个'#fff' tooltip={[ 'timeString*percent', (timeString, percent) => { return { value: percent, name: timeString, title: `${timeString}:${percent}%`, }; }, ]}// 靠这个救活了自定义的Tooltip (这个好像我之前写过在前边博客,这里就不重复写了) />
-------------------------------分割线-------------------------------------------------------------------
领导让手写这个功能了,下边附上代码
return (
<>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '12px',
margin: '10px 10px 5px 10px',
flexWrap: 'wrap',
}}
>
{minTypes.map((item) => {
const color = minTypes.includes(item) ? colorList[item].color : 'rgba(25, 34, 59, 0.2)';
const bgColor = geomList.filter((geom) => geom.text === item)[0].visible ? colorList[item].color : '#C7C8CA';
const checked = geomList.filter((geom) => geom.text === item)[0].visible;
const disabled = !minTypesData.filter((i) => i.text === item)[0].visible;
return (
<div
style={{
border: `2px solid ${bgColor}`,
backgroundColor: checked ? `${color}0b` : undefined,
cursor: disabled ? 'not-allowed' : 'pointer',
}}
className={styles.legendBox}
onClick={disabled ? undefined : toggleGeom}
data-type={item}
key={item}
>
<div className={styles.legendContain} data-type={item}>
<div
data-type={item}
style={{
width: '10px',
height: '10px',
backgroundColor: bgColor,
marginRight: '10px',
}}
/>
<div data-type={item}>{colorList[item].name}</div>
</div>
</div>
);
})}
</div>
<div style={{ overflow: 'auto', height: 190 }}>
{datas.map((item, key) => (
<div className={styles.yAxis} key={key}>
<div className={styles.yAxisText}>{item.date}</div>
<div className={styles.yAxisline}>
{item.useData.map((useItem, index) => {
if (!useItem) return <></>;
const content = (
<div>
<div className={styles.hoverLine}>
<span className={styles.hoverLeft}>
{formatMessage({ id: 'space.phoneBoothAnalysis.usage-time' })}:
</span>
<span>{useItem?.timeString}</span>
</div>
<div className={styles.hoverLine}>
<span className={styles.hoverLeft}>
{formatMessage({ id: 'space.phoneBoothAnalysis.duration-proportion' })}:
</span>
<span>{useItem.percent}%</span>
</div>
</div>
);
const widthPercent = ((useItem.endMins - useItem.startMins) / DAY_MINUTES) * 100;
const startPercent = (useItem.startMins / DAY_MINUTES) * 100;
return (
<Popover content={content} trigger="hover" key={`${index} + ${useItem.startMins}`}>
<span
className={styles['time-box']}
style={{
background: colorList[useItem.minType]?.color || 'transparent',
width: `${widthPercent}%`,
left: `${startPercent}%`,
}}
></span>
</Popover>
);
})}
</div>
</div>
))}
</div>
<div className={styles.xAxis}>
{xAxis.map((item, index) => {
const interval = 663 / 12; // 根据原X轴计算的间隔
return (
<div className={styles.xAxisline} style={{ left: interval * (index + 1) }} key={index}>
<span className={styles.xAxisNumber}>{item}</span>
</div>
);
})}
</div>
</>
);
};
style{
.yAxis {
color: #9aa9b5;
font-size: 12px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
}
.yAxisText {
margin-right: 20px;
width: 38px;
}
.yAxisline {
height: 1px;
width: 663px;
background: #f4eded;
position: relative;
}
.time-box {
height: 6px;
border-radius: 5px;
position: absolute;
top: -3px;
}
.hoverLine {
width: 100%;
font-weight: 400;
display: flex;
justify-content: space-between;
font-size: 14px;
line-height: 30px;
color: #19223b;
margin-bottom: 0;
}
.hoverLeft {
margin-right: 12px;
display: inline-block;
min-width: 120px;
}}
截止现在,这个需求就写完了,有什么问题可以留言哦~