Progress bar approaching two values

Progress bar slowly approaches two estimated values. Use case is a queued job, where the average time of the job and average time spent in queue are both known.

by thirdender

HTML

<script src="//code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
<script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<progress></progress>
<div></div>

SCSS

body {
  margin: 20px;
}

progress {
  appearance: none;
  position: relative;
  border: none;
  height: 20px;
  width: 100%;
  border-radius: 10px;
  overflow: hidden;
  background: #e4e4e4;
  color: lightblue;
  &::-webkit-progress-bar {
    background-color: #e4e4e4;
  }
  &::-webkit-progress-value {
    transition: width 0.3s ease;
    background-color: lightblue;
  }
  &::-moz-progress-bar {
    transition: width 0.3s ease;
    background-color: lightblue;
  }
}

JavaScript

const bar = document.querySelector('progress');
const output = document.querySelector('div');

// Sigmoid function, constrains a number between 0 and 1
const sig = (x) => 1 / (1 + Math.pow(Math.E, -x));
// Decimal to sigmoid, constrains values of 0 and Infinity to between 0 and 1
const decimalSig = (x) => (sig(x * Math.E) - 0.5) * 2;

const averageQueuedDuration = 1200;
const averageScanDuration = 800;

const queuedAt = new Date();
const startedAt = new Date(+queuedAt + 2400);
const completedAt = new Date(+startedAt + 1600);

const max = averageQueuedDuration + averageScanDuration;
// bar.setAttribute('max', max); // Hi Max!
bar.setAttribute('max', 100);

setInterval(() => {
  const now = +(new Date());
  const isQueued = true;
  const isStarted = now >= startedAt;
  const isCompleted = now >= completedAt;
  let value;
  if (isCompleted) {
    value = max;
  } else if (isStarted) {
    const remaining = (now - startedAt) / averageScanDuration;
    value = (decimalSig(remaining) * averageScanDuration) + averageQueuedDuration;
  } else {
    const remaining = (now - queuedAt) / averageQueuedDuration;
    value = decimalSig(remaining) * averageQueuedDuration;
  }
  bar.setAttribute('value', (value / max) * 100);
}, 100)