Настройка Масштаба

Вы можете настроить масштабы, используя свойство scales. Чтобы включить несколько масштабов, просто добавьте объекты масштаба в массив scales в конфигурации:

// Пример масштаба одного дня
gantt.config.scales = [
    {unit: "day", step: 1, format: "%j, %D"}
];
 
// Пример с несколькими масштабами
gantt.config.scales = [
    {unit: "month", step: 1, format: "%F, %Y"},
    {unit: "week", step: 1, format: weekScaleTemplate},
    {unit: "day", step:1, format: "%D", css:daysStyle }
];

Временной масштаб (ось X) может быть настроен несколькими способами:

  1. Единица измерения
  2. Диапазон
  3. Шаг
  4. Высота
  5. Формат
  6. Стиль

Также у вас есть возможность создать пользовательский масштаб.

Единицы времени

Свойство unit в объекте масштаба определяет единицу времени для масштаба.

Доступные единицы: "minute", "hour", "day" (по умолчанию), "week", "quarter", "month", и "year".

gantt.config.scales = [
    {unit: "month", step: 1, format: "%F, %Y"},
    {unit: "day", step: 1, format: "%j, %D"}
];
 
gantt.init("gantt_here");

Related sample:  Month view

Диапазон

Настройки диапазона по умолчанию

Если вы не задаете явно диапазон дат, Gantt автоматически рассчитывает его на основе загруженных дат задач. Он добавляет небольшую подкладку перед самой ранней задачей и после самой поздней, как определено настройками временного масштаба. Размер этого смещения зависит от значения scale_offset_minimal. Оно может совпадать с атрибутом unit в конфигурации масштаба или использовать наименьшую единицу временного масштаба.

Чтобы проверить отображаемый диапазон дат, вы можете использовать метод gantt.getState():

var state = gantt.getState();
 
console.log(state.min_date);
// -> Mon Jan 01 2018 00:00:00
 
console.log(state.max_date);
// -> Tue Jan 01 2019 00:00:00

Диапазон масштаба автоматически обновляется каждый раз, когда Gantt рендерится. Однако, если пользователь перемещает задачу за пределы видимого диапазона, её строка все равно будет отображаться, но полоса задачи не появится до полной перерисовки диаграммы.

Чтобы масштаб автоматически подстраивался, вы можете включить конфигурацию fit_tasks:

gantt.config.fit_tasks = true; 
gantt.init("gantt_here");

Related sample:  Auto resize scale


Явная установка диапазона дат

При необходимости вы можете вручную задать диапазон дат, используя параметры конфигурации start_date и end_date:

gantt.config.start_date = new Date(2018, 02, 31);
gantt.config.end_date = new Date(2018, 03, 09);
 
gantt.init("gantt_here");

Кроме того, вы можете указать эти даты непосредственно при инициализации:

gantt.init("gantt_here", new Date(2018, 02, 31), new Date(2018, 03, 09));

Related sample:  Define displayed date range

Задачи, которые выходят за пределы заданного диапазона, не будут отображаться на диаграмме Gantt, если они не являются незапланированными задачами.

Related sample:  Show Unscheduled Tasks

Примечание

Если заданы оба параметра start_date и end_date, задачи, созданные за пределами этого диапазона, не будут отображаться на диаграмме. Чтобы включить такие задачи, включите конфигурацию show_tasks_outside_timescale:

gantt.config.start_date = new Date(2019, 02, 31);
gantt.config.end_date = new Date(2019, 03, 09);
gantt.config.show_tasks_outside_timescale = true;
 
gantt.init("gantt_here");

Кроме того, вы можете динамически расширять диапазон:

gantt.attachEvent("onLightboxSave", function(id, task, is_new){
 var taskStart = task.start_date;
 var taskEnd = task.end_date;
 var scaleStart = gantt.config.start_date;
 var scaleEnd = gantt.config.end_date;
 
 if(scaleStart > taskEnd || scaleEnd < taskStart ){
  gantt.config.end_date = new Date(Math.max(taskEnd.valueOf(), scaleEnd.valueOf()));
  gantt.config.start_date = new Date(Math.min(taskStart.valueOf(), scaleStart.valueOf()));
  gantt.render();
 }    
 return true;
});

Или вы можете добавить валидацию, чтобы предотвратить сохранение задач за пределами диапазона:

gantt.attachEvent("onLightboxSave", function(id, task, is_new){
    var taskStart = task.start_date;
    var taskEnd = task.end_date;
    var scaleStart = gantt.config.start_date;
    var scaleEnd = gantt.config.end_date;
 
    if(scaleStart > taskEnd || scaleEnd < taskStart ){
        gantt.message({
            type: "warning", 
            text: "Warning! The task is outside the date range!",
            expire: 5000
        });
        return false;
    } 
    return true;
});

Динамическое изменение отображаемого диапазона

Существует несколько способов изменить отображаемый диапазон на лету:

  1. Используйте конфигурации start_date и end_date, обновляя их динамически, чтобы включить загруженные задачи. Например, вы можете пересчитать диапазон масштаба во время перерисовки:
gantt.attachEvent("onBeforeGanttRender", function(){
   var range = gantt.getSubtaskDates();
   var scaleUnit = gantt.getState().scale_unit;
   if(range.start_date && range.end_date){
     gantt.config.start_date = gantt.calculateEndDate(range.start_date, -4, scaleUnit);
     gantt.config.end_date = gantt.calculateEndDate(range.end_date, 5, scaleUnit);
   }
});
 
gantt.init("gantt_here");
  1. Включите свойство fit_tasks, чтобы автоматически подстраивать масштаб всякий раз, когда задачи выходят за пределы видимого диапазона:
gantt.config.fit_tasks = true; 
gantt.init("gantt_here");

Если заданы оба параметра start_date и end_date, вам потребуется комбинировать это с другими методами, чтобы fit_tasks работал корректно.

  1. Изменяйте масштаб динамически во время перетаскивания задачи. Это можно сделать, добавив логику в обработчик события onTaskDrag:
gantt.attachEvent("onTaskDrag", function(id, mode, task, original){
 var state = gantt.getState();
 var minDate = state.min_date,
     maxDate = state.max_date;
 
 var scaleStep = gantt.date.add(new Date(), state.scale_step, state.scale_unit) - new Date();
 
 var showDate,
     repaint = false;
  if(mode == "resize" || mode == "move"){
    if(Math.abs(task.start_date - minDate) < scaleStep){
      showDate = task.start_date;
      repaint = true;
    } else if(Math.abs(task.end_date - maxDate) < scaleStep){
      showDate = task.end_date;
      repaint = true;
    }
 
    if(repaint){
      gantt.render();
      gantt.showDate(showDate);
    }
  }
});

Related sample:  


Отображение задач за пределами явного диапазона дат

Задачи за пределами заданного диапазона дат все же могут быть отображены, если включить конфигурацию show_tasks_outside_timescale:

var data = {
  "tasks": [
    {"id": 1, "text": "Project #1", "start_date": "01-09-2018", "end_date": "02-09-2018"},
    {"id": 2, "text": "Project #2", "start_date": "01-09-2021", "end_date": "02-09-2021"},
    {"id": 3, "text": "Task #1", "start_date": "03-02-2020", "end_date": "05-02-2020"},
  ],
  "links": []
};
 
gantt.config.show_tasks_outside_timescale = true;
 
gantt.init("gantt_here", new Date(2020, 1, 1), new Date(2020, 2, 1));

Related sample:  Tasks outside timescale

Задачи, которые выходят за пределы диапазона, будут отображаться пустыми строками в шкале времени и все же показывать свои детали в гриде.


[Продолжайте с следующими разделами, чтобы узнать больше о временном шаге, высоте, формате даты, стиле и пользовательских единицах времени.]

Пример 1

Вот как можно определить единицу "fiscal_year", если финансовый год заканчивается 31 января. Этот фрагмент демонстрирует настройку:

var firstMonth = 1,
    firstDay = 1;
 
gantt.date.fiscal_year_start = function(date){          var next = new Date(date);
   if(next.getMonth() < firstMonth || 
      (next.getMonth() === firstMonth && next.getDate() < firstDay)){
      next = gantt.date.add(next, -1, "year"); 
   }
 
  next = gantt.date.year_start(next);
  next.setMonth(firstMonth);
  next.setDate(firstDay);
 
  return next;
}; 
 
gantt.date.add_fiscal_year = function(date, inc){       return gantt.date.add(date, inc, "year");
};

Как только это определено, вы можете интегрировать это в вашу конфигурацию следующим образом:

var dateToStr = gantt.date.date_to_str("%Y");
function fiscalYearLabel(date){
    return dateToStr(gantt.date.fiscal_year_start(date));
};
 
gantt.config.scales = [
  {unit:"year", step:1, format:"Calendar year %Y"},
  {unit:"fiscal_year", step:1, format:fiscalYearLabel},
  {unit:"month", step: 1, format: "%M %Y"},
  {unit:"day", step: 1, format:"%d %M"}
];

Пример 2

Вы можете разделить каждую ячейку "day" на три ячейки "hour", обозначенные как 00, 08 и 16. Вот как можно реализовать эту логику:

gantt.date.hour_custom_start = function (date) {
    return date;
};
 
gantt.date.add_hour_custom = function (date, inc) { // inc зависит от "step" 
    const nextDate = new Date(date);
    if (nextDate.getHours() % 8 != 0) { // значение часа не 0, 8 или 16         const diff = Math.abs(8 - nextDate.getHours());         return gantt.date.add(nextDate, diff * inc, "hour");     }     return gantt.date.add(date, 8 * inc, "hour"); };
 
gantt.config.scales = [
    { unit: "day", step: 1, date: "%d %F" },
    { unit: "hour_custom", step: 1, date: "%H" },
];
 
gantt.config.date_grid = "%Y-%m-%d %H:%i"

Related sample:  Custom hours on the scale

custom_scale

Чтобы сгенерировать первую ячейку "hour", Gantt корректирует время на основе времени начала задачи. Например, если самая ранняя задача начинается в 07:00, что не является кратным восьми, Gantt применяет это правило:

if (nextDate.getHours() % 8 != 0) {
    const diff = Math.abs(8 - nextDate.getHours());  // 8 - 7 = 1
    return gantt.date.add(nextDate, diff * inc, "hour"); // 7 - 1 = 6
}
  • Он вычисляет разницу между 08:00 и 07:00:
    diff = 08:00 - 07:00 = 1 час

  • Затем он умножает интервал на приращение:
    diff * inc = 1 час * (-1) = -1 час
    Значение приращения (inc) здесь отрицательное (-1).

  • Наконец, он корректирует время самой ранней задачи:
    07:00 + (- 1 час) = 06:00
    Значение первой ячейки становится 06.

Вторая ячейка "hour" вычисляется аналогично, но с положительным приращением:

  • diff = 08:00 - 06:00 = 2 часа

  • diff * inc = 2 часа * 1 = 2 часа

  • 06:00 + 2 часа = 08:00
    Значение второй ячейки 08.

Как только достигнуто 08:00, что является кратным восьми, последующие ячейки следуют шаблону: 08:00 + 8 часов = 16:00 и так далее.

Этот подход работает, потому что диапазон дат не определен явно.

Для получения дополнительных примеров обратитесь к руководству Как добавить пользовательский масштаб.


Пользовательские временные интервалы

Этот раздел предоставляет примеры настройки временного масштаба для включения или исключения нерабочих часов. Также рассматривается скрытие ячеек с нерабочими часами в начале шкалы даже при активном режиме skip_off_time.

Вот типичная настройка, где рабочие часы с 08:00 до 12:00 и с 13:00 до 17:00:

gantt.date.day_custom_start = function (date) {
    return date;
};
 
gantt.date.add_day_custom = function (date, inc) {     const nextDate = new Date(date);     if (nextDate.getHours() < 8) {  // Условие 1        const diff = 8 - nextDate.getHours();         return gantt.date.add(nextDate, diff * inc, "hour");     }     if (nextDate.getHours() == 8) {  // Условие 2        return gantt.date.add(nextDate, 9 * inc, "hour");     }     if (nextDate.getHours() == 17) {  // Условие 3        return gantt.date.add(nextDate, 15 * inc, "hour");     }  
    return gantt.date.add(date, 8 * inc, "hour"); };
 
gantt.config.scales = [
    { unit: "day_custom", step: 1, date: "%d %H:00" },
];
 
// gantt.config.skip_off_time = true;
gantt.config.work_time = true;
gantt.config.correct_work_time = true;
gantt.plugins({
    auto_scheduling: true,
});
gantt.setWorkTime({ hours: ["8:00-12:00", "13:00-17:00"] });  
gantt.config.duration_unit = "minute";
gantt.config.duration_step = 1;
gantt.config.time_step = 1;
gantt.config.round_dnd_dates = false;

Related sample:  Custom time spans

Предположим, что самая ранняя задача начинается в 08:00 1 апреля 2025 года. В зависимости от настройки gantt.config.skip_off_time Gantt корректирует временной масштаб по-разному.

Когда нерабочие часы скрыты:

gantt.config.skip_off_time = true;

Gantt вычисляет первую ячейку "hour", уменьшая время самой ранней задачи до тех пор, пока оно не совпадет с рабочими часами предыдущего дня:

  • Он вычитает 9 часов из 08:00 1 апреля 2025 года (Условие 2):
    08:00 - 9 часов = 23:00
  • Так как 23:00 является нерабочим временем, он вычитает еще 8 часов:
    23:00 - 8 часов = 15:00
  • Полученное время, 15:00 31 марта 2025 года, находится в пределах рабочих часов.

Таким образом, значение первой ячейки становится 31 15:00.

Когда нерабочие часы отображаются:

gantt.config.skip_off_time = false;

Первая ячейка все равно начинается с 31 15:00, но перед самой ранней задачей появляются дополнительные пустые ячейки. Эти ячейки представляют собой нерабочие часы.

Например:

  • Вторая ячейка: 15:00 + 8 часов = 23:00
  • Третья ячейка: 23:00 + 8 часов = 07:00
  • Четвертая ячейка: 07:00 + 1 час = 08:00 (время самой ранней задачи).

Остальные ячейки рассчитываются аналогично.


Если вы хотите иметь устойчивое смещение одной ячейки независимо от настройки skip_off_time, вы можете использовать эту логику:

gantt.date.add_day_custom = function (date, inc) {
    // Для загруженных задач вычислите дату первой ячейки
    if (inc < 0 && gantt.getTaskByTime().length) {
        return gantt.calculateEndDate({ 
            start_date: date, duration: -1, unit: gantt.config._duration_unit 
        })
    }
 
    // Вычислите время начала и конца рабочего дня
    if (date.getHours() == 8) {
        return gantt.calculateEndDate(date, 8);
    }
    if (date.getHours() == 17) {
        return gantt.date.add(date, 15 * inc, "hour");
    }
 
    // Для задач или полного масштаба вычислите даты соответственно
    date = gantt.date.add(date, 1 * inc, "day");
    gantt.date.day_start(date);
    date = gantt.getClosestWorkTime({ date, dir: "future" })
    return date
};
 
gantt.config.scales = [
    { unit: "day_custom", step: 1, date: "%d %H:%i" },
];
gantt.config.work_time = true;
 
gantt.config.skip_off_time = false;

Related sample:  Equal offset for custom scales

Вот как это выглядит, когда нерабочие часы скрыты:

custom_first_scale_cell

И когда они показаны (отключен skip_off_time):

first_scale_cell_without_skip_off_time


Бесконечная прокрутка

Для получения подробных примеров реализации бесконечной прокрутки в шкале времени обратитесь к соответствующей статье.


Липкие метки

Начиная с версии 9.0, метки временного масштаба по умолчанию являются липкими. Это обеспечивает видимость меток при прокрутке по шкале времени, улучшая читаемость, особенно при увеличении или уменьшении масштаба.

Чтобы отключить эту функцию и вернуть центрированные метки, установите sticky в false:

gantt.config.scales = [
  {unit: "year", step: 1, format: "%Y", sticky: false},
  {unit: "month", step: 1, format: "%F", sticky: false},
  {unit: "day", step: 1, format: "%j", sticky: false}
];
gantt.init("gantt_here");

Чтобы заставить метки оставаться липкими независимо от ширины ячейки, установите sticky в true:

gantt.config.scales = [
  {unit: "year", step: 1, format: "%Y", sticky: true},
  {unit: "month", step: 1, format: "%F", sticky: true},
  {unit: "day", step: 1, format: "%j", sticky: true}
];
gantt.init("gantt_here");
К началу