注册
web

无插件实现一个好看的甘特图

效果


预览地址 code.juejin.cn/pen/7272286…


QQ录屏20230828153807 00_00_00-00_00_30.gif


前言


刚好看到这么一个东西,甘特图,然后又发现好像echarts 里面没有这个图形渲染,也去找了一下插件,都不符合我的要求,然后自己就想着看能不能实现一个,然后说干就干,过程还挺复杂的,不过一想清楚了,也就还行。


逻辑


刚开始看着这个图,想到肯定是用表格来实现的,后面开始渲染的时候,发现是我想简单了,这表格是渲染不出来的,也或者是我技术还没够,反正我找了很多相关的插件,都不是用表格去实现的,后面就改变了一些思路,用div去渲染,然后去定位,这么一想,发现事情就简单了许多。


为什么不用表格实现


每点击一个视图切换,表头就需要重新渲染,表头有索引,名称,负责人,如果是表格实现的话,就是如下代码,一看就知道了id=“tableYear”的不好渲染,因为上面有可能是年,也有可能是月,所以肯定是有循环的,但如果一循环,它们没有共同的父容器,怎么渲染呢,也想过表格里面套表格,但那样,样式又实现不了我要的效果。


 <table>
<thead>
<tr>
<th rowspan="2">id</th>
<th rowspan="2">任务名称</th>
<th rowspan="2">负责人</th>
<th colspan="4" id="tableYear">2023-8</th>
</tr>
<tr id="tableDay">
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
</tr>
</thead>
</table>

第一个难点


日期渲染,因为我这表头是动态的,所以要复杂一些,有天数,月数,季度和年度显示的


image.png
点击日视图,就是按天去显示任务,月视图就是按月去显示任务,季视图和年视图同理,
比如说日视图,
先获取当前日期,年月日,然后是想渲染前后几年的


var currentDate=new Date;//当前日期
var currentYear = currentDate.getFullYear();//当前年份

var yearRange = 1; // 前后1年
var startDate = currentYear - yearRange;//前1年
var endDate = currentYear + yearRange;//后1年
var today = currentDate.getDate(); // 获取今天是几号
var currentYear = currentDate.getFullYear();//年
var currentMonth = currentDate.getMonth();//月
var displayedYears = {}; // 用于记录已显示的年份

开始渲染第一排是年月,一年的月份是固定的,所以都是可以写死的,这里有要注意一下的是,就是年月的宽度,因为要根据当月有多少天去计算宽度,所以我要知道,这一年这一月是多少天然后乘以40 下面是相关代码


for (var year = startDate; year <= endDate; year++) {
for (var month = 0; month < 12; month++) {
var lastDay = new Date(year, month + 1, 0).getDate();
var monthElement = $("<p>" + (month + 1) + "月: </p>"); // 创建表示月份的 <p> 元素

for (var day = 1; day <= lastDay; day++) {
dateRange.push(new Date(year, month, day));
}
// 在 .tableYear 中添加年份和月份信息
var yearMonthStr = year + "-" + (month + 1 < 10 ? "0" : "") + (month + 1);
var width = (lastDay * 40)-1 + "px"; // 计算宽度
$(".tableYear").append($("<p class='Gantt-table_header' style='width: " + width + "'>" + yearMonthStr + "</p>"));
}
}

渲染完成,年月以后就是日,我这里也做了一些小的显示,比如说周末深颜色表示,今天也深颜色表示,并且视图要显示在当前,而不是在2022-1-1号这里。


天数渲染


for (var i = 0; i < dateRange.length; i++) {
var currentDate = dateRange[i];
var dayNumber = currentDate.getDay(); // 获取星期几 (0 = 星期日, 1 = 星期一, ...)
var isWeekend = dayNumber === 0 || dayNumber === 6;
var dayText = currentDate.getDate();//获取日
var dayYear = currentDate.getFullYear(); // 获取年份
var dayMonth = currentDate.getMonth(); // 获取月份(注意:月份是从 0 到 11,0 表示一月,11 表示十二月)
var tableCell = $("<p>" + dayText + "</p>");
if (isWeekend) {
tableCell = $("<p class='Gantt-table_weekend'>" + dayText + "</p>");
}
//获取当前时间的年月日,与循环出来的年月日进行循环匹配,
if (dayText === today && dayYear === currentYear && dayMonth === currentMonth ) {
tableCell.addClass("today");
}
$(".tableDay").append(tableCell);
}

视口显示代码


 // 将视口滚动到今天所在的位置
var todayElement = $(".today");
if (todayElement.length > 0) {
var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
var elementHeight = todayElement[0].clientHeight;

var offset = (viewportHeight - elementHeight) / 2;
//平滑滚动参数 smooth auto不滚动
todayElement[0].scrollIntoView({ behavior: 'auto', block: 'center', inline: 'center' });
}

第二个难点


image.png
如果不用表格,怎么去实现这个网格呢,刚开始是想着去渲染,表格对应有多少天,就渲染有多少个p标签,但一想这怎么行,如果任务很多,一条任务就要渲染上千个p标签,太浪费资源了,任务一多,那不直接挂壁。
后面在css里面找到了解决办法


background: repeating-linear-gradient(to right, rgb(221, 221, 221), rgb(221, 221, 221
1px, transparent 1px, transparent 40px) 0% 0% / 40px 100%;

ChatGPT是这样解释的


image.png
然后完美解决问题,不用渲染这么多,一个任务一个div就可以了。


第三个难点


甘特图的核心,那个柱状图的东西。


image.png


柱状图渲染,比如说我提了一个任务,是2023年8月20号开始的,然后到2023年8月25号要完成,那这样就只有五天时间,那渲染肯定是从2023年8月20号开始的,然后到8月25号结束。


我的思路是这样的,显示的宽度是五天,然后一天的宽度是40px,那么这个任务的总宽度就是200px,然后定位,获取到这个任务开始的时候,我这里显示是前后一年的也就是2022-1-1号开始的,然后相减,知道中间相差了多少天,然后再乘以40,得到left的距离,就实现了这个效果。后面的描述也是类似的效果,这样甘特图差不多就完成了。下面是日期相减代码


 function getOffsetDays(startDate, endDate){
var startDateArr = startDate.split("-");
var checkStartDate = new Date();
checkStartDate.setFullYear(startDateArr[0], startDateArr[1], startDateArr[2]);
var endDateArr = endDate.split("-");
var checkEndDate = new Date();
checkEndDate.setFullYear(endDateArr[0], endDateArr[1], endDateArr[2]);
var days = (checkEndDate.getTime() - checkStartDate.getTime())/ 3600000 / 24;
if(startDateArr[0]!=endDateArr[0]){
flag = true;
}
return days;
}

结语


虽然三言两语就讲解完了,但其中还是有很多逻辑的问题,我只是讲解了一下我的一个实现的思路,居体代码请看这


以上就是本篇文章的全部内容,希望对大家的学习有所帮助,以上就是关于无插件实现一个好看的甘特图的详细介绍,如果有什么问题,也希望各位大佬们能提出宝贵的意见。当然也有很多需要优化的地方,我这只是给了一个思路,你们可以去实现很多的功能。


作者:流口水的兔子
来源:juejin.cn/post/7272174836336132132

0 个评论

要回复文章请先登录注册