A Flexible Countdown Timer

Creating a countdown timer clock is a fairly common task, and often used in timed promotions. There’s a lot of solutions on the internet and even on these forums. I wanted to try creating one that would give content creators a lot of flexibility with the goal being able to use your own styling on the various time components.

Here’s my solution for a flexible countdown timer. You can see it in action with this document:

FlexibleCountdownTimer.hype.zip (65.8 KB)

First, copy this code to the Head HTML:

<script>

// replace the string value with the end date you want for the countdown timer
// this is an ISO 8601 string, like: YYYY-MM-DD(T)HH:MM:SS(+/-)TZD

var countDownDateString = "2017-05-15T00:00:00-08:00";


function updateCountdownClocks() {
	// some code from:
	//    https://www.w3schools.com/howto/howto_js_countdown.asp
	// 	  http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript

	var targetTimeInterval = new Date(countDownDateString).getTime();
	var currentTimeInterval = new Date().getTime();
	
	var deltaTimeInterval = targetTimeInterval - currentTimeInterval;
	if(deltaTimeInterval < 0) {
		// do special handling when timer is expired; default is to just set it to 0
		deltaTimeInterval = 0;
	}
	
	var timeComponents = {
		"days" : Math.floor(deltaTimeInterval / (1000 * 60 * 60 * 24)),
		"hours" :  Math.floor((deltaTimeInterval % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
		"minutes" : Math.floor((deltaTimeInterval % (1000 * 60 * 60)) / (1000 * 60)),
		"seconds" : Math.floor((deltaTimeInterval % (1000 * 60)) / 1000),
	};
	
	// utility function to add leading 0's
	function pad(n, width, z) {
		z = z || '0';
		n = n + '';
		return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
	}
	
	// utility function that gets the requested padding of zeros from a class name
	// of the format countdown_format_N where 'N' is an integer for the 
	// total width (ie 3 would be 088 008 888 8888)
	function determinePadding(element) {
		var matches = element.className.match(/countdown\_format\_([\d]+)/);
		if(matches == null || matches.length != 2) {
			return 0;
		}
		return Math.max(0, parseInt(matches[1]));
	}

	// for each component, try to find a matching class name that wants to be filled in
	for(var timeComponentKey in timeComponents) {
		if(timeComponents.hasOwnProperty(timeComponentKey) == false) {
			continue;
		}
		
		var timeValue = timeComponents[timeComponentKey];
		
		var timeElements = document.getElementsByClassName("countdown_" + timeComponentKey);
		for(var i = 0; i < timeElements.length; i++) {
			var timeElement = timeElements[i];
			var padding = determinePadding(timeElement);
			timeElement.innerHTML = pad(timeValue, padding);
		}
	}
	
	// repeat every second
	window.setTimeout(updateCountdownClocks, 1000);
}

// start after document has loaded (without use of handlers)
window.setTimeout(updateCountdownClocks, 0);

// use a Hype scene load handler to start:
if("HYPE_eventListeners" in window === false) {
	window.HYPE_eventListeners = Array();
}
window.HYPE_eventListeners.push({"type":"HypeSceneLoad", "callback":updateCountdownClocks});

</script>

In the above paste, you’ll want to change this line to reflect the date you are counting down to:

var countDownDateString = "2017-05-15T00:00:00-08:00";

You don’t need to modify anything else in the JavaScript code.

The way the countdown timer works is that it will replace the inner HTML of elements with these class names to their various values: countdown_days, countdown_hours, countdown_minutes, and countdown_seconds. So what you can do is make text elements in Hype and set the Class Name value in the Identity Inspector to each of these.

A common formatting problem with time is whether or not to display leading zeros. If you append another class name of countdown_format_2 then the value will always display two or more digits (in fact, you can use any number as the class name, so you could use countdown_format_3 for something like the countdown_days if you wanted). So a full Class Name might look like:

Because this just operates on class names, you don’t necessarily need to break out each component into a separate Hype element. It can also look into the inner HTML of a single element. So if you wanted to construct a timer that looked like “0d 0h 00m 00s”, you would set the Inner HTML of the element to this code:

<span class="countdown_days">0</span>d
<span class="countdown_hours">0</span>h
<span class="countdown_minutes countdown_format_2">00</span>m
<span class="countdown_seconds countdown_format_2">00</span>s

The code itself doesn’t really have any Hype dependencies, so you could even use it on non-Hype projects if you wanted, but hopefully the way this was put together will be useful when using Hype for countdown timers!

P.S. There are some items this countdown timer doesn’t handle:

  • When the countdown is done, the value is simply 0
  • There’s no way to represent other date values, like weeks or months
  • While you can have as many different views on the countdown date as you want, there can only be one date per document/html page.
12 Likes

Would this work correctly for weeks.
Seems to be ok when I test.

var timeComponents = {
	"weeks" : Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24))/7),
		"days" : Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24) - (Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24))/7)) *7 ) ),
		"hours" :  Math.floor((deltaTimeInterval % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
		"minutes" : Math.floor((deltaTimeInterval % (1000 * 60 * 60)) / (1000 * 60)),
		"seconds" : Math.floor((deltaTimeInterval % (1000 * 60)) / 1000),
	};

Added a element with the class names countdown_weeks countdown_format_1

The reasons I didn’t add weeks is presumably you’d want a different value for days (the remainder). This gets far more complex if you want months, years, etc.

Yep , although the code above does give you the remaining days., my head started to hurt just trying to work that one out, hence my questioning it as I my brain was fried ):smile:

Oh, I missed that since I was looking for a different key name :slight_smile:.

1 Like

Hi, I like the countdown clock, but what is the reason to copy the js into the head element?
It also works fine as a document js when you trigger it on scene load.

1 Like

I probably just had that as a lowest common denominator type of suggestion such that if people moved around scenes or had multiple clocks across different scenes it would still work without duplicating code/variables. If using the code within an On Scene Load handler is better for you, by all means do it that way :).

Hi Jonathan,

At the end of the set time, what about if I want to redirect the countdown page to a new URL in this sample please? do I still need using timeline action of “go to URL”?

Thanks

See the section of the code:

	if(deltaTimeInterval < 0) {
		// do special handling when timer is expired; default is to just set it to 0
		deltaTimeInterval = 0;
	}

In here you can place code if you want to go to a different URL. You could either fire a timeline with the action, or from a more javascript-y approach just use:

        window.location.href = "http://www.example.com";
1 Like

Hello,

Thanks for your code it is what I was looking for. Is it possible to display always two digits? I mean 21:01:25 instead of 21:1:25

Regards.

The code should already do this through the pad() function. Can you send a screenshot and perhaps the time of it not working?

Sorry, it is working now.

Thanks.

1 Like

Mechanical Letter Countdown Timer

Demo:
MechanicalLetterCountdownTimer.html

Download:
MechanicalLetterCountdownTimer.hype.zip

Based on the work of @jonathan

3 Likes

Great! anyway to trigger a redirection of specific url after countdown end?

It fires a custom behavior called “timerExpired”. You can do what ever you want with that. Make sure you have the latest version.

Great, that would be more easy to custom style by Hype and trigger something.

Great work Max!

1 Like

Hi…Given the lack of accuracy, I opted to go with basic math and define 2 variables: mn and sec and decrement via a 1 sec delay.
Was able to make that approach work and print via serial to a 16x2 LCD. Got the minutes (mn) to print first, then a colon, then the sec. And it decremented correctly.
Then acquired a 4 digit 7 segment LED display with backpack. Assembled same and it performed the test functions correctly. On to trying to get it to display what I want.

complete pcb

Not sure what lack of accuracy your are referring to.

1 Like

Thanks man, i will use it in my web game!