Saturday, 2 October 2021

Vis JS Timeline - Freeze horizontal time axis when vertically scrolling

I'm using the following options in Vis JS Timeline to produce a horizontal axis at the top of the timeline with time labels:

orientation: {
    axis: 'both'
},

The horizontal axis looks like this:

enter image description here

My timeline has many rows, so the user needs to vertically scroll down the page to see everything. The problem is that the horizontal axis at the top does not stay in view when scrolling down the page.

Question: How can I freeze the horizontal axis at the top so that the time labels stay in view when scrolling down?

The following code snippet, or jsfiddle.net/nj1647tb, is my timeline:

const seed = '11';
Math.seedrandom(seed);

const nGroups = 40;
const maxSubGroups = 2;
const maxItemsPerSubGroup = 1;
const metaEventCount = 2;
const itemLengthScale = 200;

let now = moment().minutes(0).seconds(0).milliseconds(0);
var groupCount = 12;
var itemCount = 70;
var tcCrashProbability = 0.2;

function randInt(min, max) {
  return Math.round(min + Math.random() * (max - min));
}

function getStartEnd(earliestStart) {
  if (earliestStart === undefined) {
    earliestStart = 0;
  }
  let startAdd = earliestStart + Math.random() * 200;
  let length = Math.random() * itemLengthScale;
  let endAdd = startAdd + length;
  return {
    startAdd: startAdd,
    endAdd: endAdd
  }
}

const stackTrace = `Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/requests/adapters.py", line 449, in send
    timeout=timeout
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 756, in urlopen
    method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
  File "/usr/local/lib/python3.6/site-packages/urllib3/util/retry.py", line 532, in increment
    raise six.reraise(type(error), error, _stacktrace)
  File "/usr/local/lib/python3.6/site-packages/urllib3/packages/six.py", line 769, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 706, in urlopen
    chunked=chunked,
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 445, in _make_request
    six.raise_from(e, None)
  File "<string>", line 3, in raise_from
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 440, in _make_request
    httplib_response = conn.getresponse()
  File "/usr/lib64/python3.6/http/client.py", line 1346, in getresponse
    response.begin()
  File "/usr/lib64/python3.6/http/client.py", line 307, in begin
    version, status, reason = self._read_status()
  File "/usr/lib64/python3.6/http/client.py", line 268, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "/usr/lib64/python3.6/socket.py", line 586, in readinto
    return self._sock.recv_into(b)
  File "/usr/lib64/python3.6/ssl.py", line 971, in recv_into
`;

// create a data set with groups
var group_names = [];
var groups = new vis.DataSet();
for (let i = 0; i < nGroups; i++) {
  group_names.push('GROUP_' + i);
  groups.add({
    id: group_names[i],
    content: group_names[i]
  });
}

// add meta group
groups.add({
  id: -1,
  content: '&nbsp;'
});

// create a dataset with items
let items = new vis.DataSet();
for (let i = 0; i < nGroups; i++) {
  let nSubGroups = randInt(1, maxSubGroups);
  //console.log('group='+i+' nSubGroups='+nSubGroups);
  let lastStartAdd = 0;
  for (let sg = 0; sg < nSubGroups; sg++) {
    let start_end = getStartEnd(lastStartAdd);
    let start = now.clone().add(start_end['startAdd'], 'hours');
    let end = now.clone().add(start_end['endAdd'], 'hours');
    let itemID = 'G' + i + '_S' + sg + '_item';
    let subgroupID = 'G' + i + '_S' + sg;
    let subgroupOrder = sg;
    let newItem = {
      id: itemID,
      group: group_names[i],
      subgroup: subgroupID,
      subgroupOrder: subgroupOrder,
      content: 'ITEM_DU_' + 'G' + i + '_S' + sg,
            start: start,
      end: end,
      title: 'ITEM_DU_' + 'G' + i + '_S' + sg
    };
    //console.log(group_names[i] + ', ' + 'S' + sg + ', ' +start_end['startAdd'] + ', ' + start_end['endAdd']);    
    items.add(newItem);
    lastStartAdd = start_end['startAdd'];
    
    // random crashes
    if(Math.random() <= tcCrashProbability) {
        let crashStart = now.clone().add(randInt(start_end['startAdd'], start_end['endAdd']), 'hours');
      let newCrashItem = {
        id: 'crash_' + itemID,
        group: group_names[i],
        subgroup: subgroupID,
        subgroupOrder: subgroupOrder,
        content: 'Crash',
        start: crashStart,
        type: 'box',
        className: 'timeline-tc-crash',
        title: '<pre>' + stackTrace + '</pre>'
      };
      items.add(newCrashItem);
    }
    
  }
}

// generate some meta events
for (let i = 0; i < metaEventCount; i++) {
  let start = now.clone().add(Math.random() * 200, 'hours');
  items.add({
    id: 'M' + i,
    group: -1,
    content: 'Crash',
    title: '<pre>' + stackTrace + '</pre>',
    className: 'timeline-event-crash',
    start: start,
    type: 'box'
  });
}

// create visualization
var container = document.getElementById('visualization');
var options = {
  groupOrder: 'content',
  stack: false,
  stackSubgroups: true,
  orientation: {
    axis: 'both'
  },
  showCurrentTime: false
};

var timeline = new vis.Timeline(container);
timeline.setOptions(options);
timeline.setGroups(groups);
timeline.setItems(items);
#visualization {
  box-sizing: border-box;
  width: 100%;
  height: 300px;
}

.timeline-event-crash {
  background-color: red !important;
  border-color: darkred !important;
  color: white !important;
  font-family: monospace;
  box-shadow: 0 0 10px gray;

}

.timeline-tc-crash {
  color: red !important;
  border-color: red !important;
  background-color: #F4BBB5 !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.10/seedrandom.min.js"></script>
<link href="https://visjs.github.io/vis-timeline/styles/vis-timeline-graph2d.min.css" rel="stylesheet" />
<script src="https://visjs.github.io/vis-timeline/standalone/umd/vis-timeline-graph2d.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

<html>

  <head>
    <title>Timeline</title>
  </head>

  <body>
    <div id="visualization"></div>
  </body>

</html>


from Vis JS Timeline - Freeze horizontal time axis when vertically scrolling

No comments:

Post a Comment