likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 8

效果

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

前言

刚好看到这么一个东西,甘特图,然后又发现好像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>

第一个难点

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

无插件实现一个好看的甘特图 点击日视图,就是按天去显示任务,月视图就是按月去显示任务,季视图和年视图同理, 比如说日视图, 先获取当前日期,年月日,然后是想渲染前后几年的

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' });
}   

第二个难点

无插件实现一个好看的甘特图 如果不用表格,怎么去实现这个网格呢,刚开始是想着去渲染,表格对应有多少天,就渲染有多少个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是这样解释的

无插件实现一个好看的甘特图 然后完美解决问题,不用渲染这么多,一个任务一个div就可以了。

第三个难点

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

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

柱状图渲染,比如说我提了一个任务,是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;
}

结语

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