countdown timer with css animation

by Stalk

HTML

<div class="timer" id="a">
	<div class="display">
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="delimeter">:</div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="delimeter">:</div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
	</div>
</div>

<div class="timer other">
	<div class="display">
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="delimeter">:</div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="delimeter">:</div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
		<div class="symbol"><div class="cur"></div><div class="old"></div></div>
	</div>
</div>

CSS

html, body{margin: 0px;background-color: #20262e;}

.timer {
  width: 210px;
  margin: 10px auto 0px auto;
  font-family: Comic Sans MS;
  font-size: 20px;
  color:  #fff;
  cursor:  default;
  text-align:  center;
  line-height: 40px;
  border-radius: 10px;
  cursor:  default;
}

.display {
  width: 180px;
  height: 50px;
  height: 90px;
  margin: 0px auto 0px auto;
  color: #ffffff;
  padding: 10px 0px 10px 0px;
}

.symbol {
  width: 27px;
  height: 50px;
  float:  left;
  overflow:  hidden;
}
.symbol div {
}
.cur,.old{
  width: 92%;
  height: 40px;
  float: left;
  background-color: #3F51B5;
  border-radius: 3px;
  margin: 5px 1px;
  transform: translateY(-50px);
  box-shadow: inset 1px 1px 3px 0px rgba(255, 255, 255, 0.5);

}
.symbol.anim div {
  transform: translateY(0px);
  transition: transform .7s ease-in-out;
}
.delimeter {
  width: 9px;
  float:  left;
  line-height: 47px;
  margin-left: 0px;
  text-indent: 0px;
}

.other .symbol div {
  background-color: tomato;
}

JavaScript

function countdown(dateStr, displayElem, onTimerEnd = null) {
  let targetDate = new Date(dateStr).getTime();
  let displaySymbols = [...displayElem.querySelectorAll('.symbol .cur')]
  let prevValue = '      '
  let timer = setInterval(function() {
    let now = new Date().getTime();
    let remainingTime = targetDate - now;
    if (remainingTime <= 0) {
      clearInterval(timer) // stop timer
      if (onTimerEnd) {
        onTimerEnd() // run callback
      }
    }

    let formattedTimestr = format(remainingTime);
    updateDisplay(formattedTimestr);
  }, 1000);

  function format(time) {
    return [time % 86400000 / 3600000, // hours
            time % 3600000 / 60000, // minutes
            time % 60000 / 1000] // seconds  
      .map(x => Math.floor(x).toString().padStart(2, '0'))
      .join('')
  }

  function updateDisplay(timeString) {
    displaySymbols.forEach((curSymbol, i) => {
      let newValue = timeString[i]
      let currentValue = prevValue[i]
      if (currentValue !== newValue) { // animated change
        let parent = curSymbol.parentNode
        parent.classList.remove('anim')
        curSymbol.textContent = newValue
        curSymbol.nextElementSibling.textContent = currentValue
        var foo = parent.offsetWidth; // reflow hack
        parent.classList.add('anim')
      }
    })
    prevValue = timeString
  }
}

countdown('2033-02-10 00:00:00', document.getElementById('a'))
countdown('2033-02-10 04:20:00', document.querySelector('.other'))