Final Flipping Countdown Clock – Part 4: Making it Work

Reading Time: 9 minutes

So after Part 3 (Part 1 and Part 2) we’ve got the JS in place to get the total time for our countdown and convert that into days, hours, minutes and seconds. Then breaking down those into their respective digits. The next step is going to be setting the date when the page loads and then actually counting down.

The first thing I’m going to do is create a set of functions to control setting the next number in the countdown. Sounds easy, but we’ve got to make sure we account for going from 0 to 5 for some digits instead of 0-9. Otherwise we’d countdown from 00 to 99 instead of 59 like we want.

function setDigit (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 9;
    }
}

function setDigitMin (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 5;
    }
}

function setDigitHour (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 3;
    }
}

function setDigitHourTenths (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 2;
    }
}

First, we’ll start with setDigit this function is going to take a parameter “digit” that we’ll pass in when we call it. If our number is greater than 0 we’re returning digit – 1, but if it equals 0 we’re returning 9. If we just stuck with the if statement when we got to 0 we’d end up with -1 and so on instead of resetting.

The following functions follow the same general rule, but are for different digits. setDigitMin returns 5 if 0 is given so we go from 00 to 59. setDigitHour returns 3 so we go from 00 to 23 hours remaining and setDigitHourTenths returns the 2.

Ok, here is where it gets a bit crazy, and admittedly could probably be worked into more concise code. The first thing we’re going to do is create  a function named setTimer and call our getDate function so we have our date variables. Then we’re going to set the initial time of the timer.

function setTimer () {
	getDate();

	// If statement to fix glitch when loading in at 0 seconds in first spot
	if (firstDigit === 0) {
		$('ul.secondPlay li').eq(0).find('.inn').html(setDigit(firstDigit));
	} else {
	    $('ul.secondPlay li').eq(0).find('.inn').html(firstDigit);
    }

}

So what we have here is a dirty fix to an issue I was having when loading in the page at say 50 seconds. Instead of dropping to 49 right away we’d drop to 59 until it caught up flipping to 39. Obviously, we can’t have that so if the first digit is 0 when we load we’re running our setDigit function on the digit so we get the proper number. Now I’m going to just post the whole block of code for the rest. It’s basically the same as this line, but with increasingly longer if statements which I’ll explain in a minute.

function setTimer () {
	getDate();

	// If statement to fix glitch when loading in at 0 seconds in first spot
	if (firstDigit === 0) {
		$('ul.secondPlay li').eq(0).find('.inn').html(setDigit(firstDigit));
	} else {
	    $('ul.secondPlay li').eq(0).find('.inn').html(firstDigit);
    }

    if (firstDigit === 0) {
		$('ul.secondTenths li').eq(0).find('.inn').html(setDigitMin(secondDigit));
    } else {
	    $('ul.secondTenths li').eq(0).find('.inn').html(secondDigit);
    }

    if (secondDigit ===0 && firstDigit === 0) {
		$('ul.minutePlay li').eq(0).find('.inn').html(setDigit(mmFirstDigit));
    } else {
	    $('ul.minutePlay li').eq(0).find('.inn').html(mmFirstDigit);
    }

    if (mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.minuteTenths li').eq(0).find('.inn').html(setDigitMin(mmSecondDigit));
    } else {
	    $('ul.minuteTenths li').eq(0).find('.inn').html(mmSecondDigit);
    }

    if (mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.hourPlay li').eq(0).find('.inn').html(setDigitHour(hhFirstDigit));
    } else {
	    $('ul.hourPlay li').eq(0).find('.inn').html(hhFirstDigit);
    }

    if (hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.hourTenths li').eq(0).find('.inn').html(setDigitHourTenths(hhSecondDigit));
    } else {
	    $('ul.hourTenths li').eq(0).find('.inn').html(hhSecondDigit);
    }

    if (hhSecondDigit === 0 && hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.dayPlay li').eq(0).find('.inn').html(ddFirstDigit);
    } else {
	    $('ul.dayPlay li').eq(0).find('.inn').html(ddFirstDigit);
    }

    if (ddFirstDigit === 0 && hhSecondDigit === 0 && hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.dayTenths li').eq(0).find('.inn').html(setDigit(ddSecondDigit));
    } else {
	    $('ul.dayTenths li').eq(0).find('.inn').html(ddSecondDigit);
    }

    $('.container').css('display', 'block');				

    setInterval(function () {
        counter()
    }, 1000);

}

setTimer();

Looks confusing but essentially each if statement is checking to see if the digits to the right all = 0 indicating it needs to avoid the issue of being set to high. If all the numbers to the digit we’re setting are 0 then it runs the digit through the proper setDigit function. Notice we only handle the first  “li” for setting. Our counter function we’re about to get to will handle the second.

Finally, at the bottom of that function we’re displaying our container (I had it hidden so it doesn’t load in oddly). Then we have a setInterval function which runs the counter function we’re about to create every 1000 milliseconds or 1 second, and outside the function we call setTimer() so it runs.

function counter () {
    getDate();

    //Calls play has to be before seconds are set
    play("secondPlay");

}

So we start off by creating our counter function and the first thing we want to do is call our getDate function. This resets all our date variables every 1000 milliseconds when the counter function is run.

The next thing we do is call our play function and pass it a string “secondPlay”. Wait, what’s play. We’ll get to that now.

function counter () {
    getDate();

    //Calls play has to be before seconds are set
    play("secondPlay");

}

function play(target) {
    $("body").removeClass("play");
    var aa = $("ul."+target+" li.active");

    if (aa.html() == undefined) {
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");

    }
    else if (aa.is(":last-child")) {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before").removeClass("active");
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("active")
            .closest("body")
            .addClass("play");
    }
    else {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");
    }

}

So if you’ve explored the CodePen example this code should look familiar. It’s what controls the flipping animation at the proper moments. Honestly we could probably get rid of some of this since we’re only going through two space, but we’ll leave it for now. We are changing up the CodePen example some though so we can reuse it for each of our digits and don’t have to create multiple functions. To do this we’re adding the parameter “target” which we’ll pass in strings which use the HTML ul class for each specific digit.

Basically this function adds the classes before and active which have our css animations. Basically it looks for the line with the class active (if no line is active it adds the class before to the first li and the class active to the next li). Then if active is the last li then it makes that before and makes the first li (eq(0)) active. Otherwise it just moves down the li making the next li active. Notice we’re using target in our selector to hit the proper uls when we call our play function.

function counter () {
    getDate();

    //Calls play has to be before seconds are set
    play("secondPlay");
    $('ul.secondPlay li.before .inn').html(firstDigit);
    $('ul.secondPlay li.active .inn').html(setDigit(firstDigit));

    // Sets second second spot when first second spot === 0
    if (firstDigit === 0) {
	   play("secondTenths");
	   $('ul.secondTenths li.before .inn').html(secondDigit);
	   $('ul.secondTenths li.active .inn').html(setDigitMin(secondDigit));
    }

}

function play(target) {
    $("body").removeClass("play");
    var aa = $("ul."+target+" li.active");

    if (aa.html() == undefined) {
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");

    }
    else if (aa.is(":last-child")) {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before").removeClass("active");
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("active")
            .closest("body")
            .addClass("play");
    }
    else {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");
    }

}

We’ll be using those if statements to check if the numbers to the right are === 0 again to determine if we need to call our play function on that particular digit. So we want our first spot (from the right) to play every 1000 milliseconds (1 second) regardless so it doesn’t have an if statement. The next digit to the left we want to play every time the first digit === 0. So when we get to 20 seconds the 2 drops down to a 1. We can’t do this with our interval function so we’ll just use logic to check if we need to flip it. Here’s the rest of those.

function counter () {
    getDate();

    //Calls play has to be before seconds are set
    play("secondPlay");
    $('ul.secondPlay li.before .inn').html(firstDigit);
    $('ul.secondPlay li.active .inn').html(setDigit(firstDigit));

    // Sets second second spot when first second spot === 0
	if (firstDigit === 0) {
	   play("secondTenths");
	   $('ul.secondTenths li.before .inn').html(secondDigit);
	   $('ul.secondTenths li.active .inn').html(setDigitMin(secondDigit));
	}

	if (secondDigit === 0 && firstDigit === 0) {
	   play("minutePlay");
	   $('ul.minutePlay li.before .inn').html(mmFirstDigit);
	   $('ul.minutePlay li.active .inn').html(setDigit(mmFirstDigit));
	}

	if (mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("minuteTenths");
	   $('ul.minuteTenths li.before .inn').html(mmSecondDigit);
	   $('ul.minuteTenths li.active .inn').html(setDigitMin(mmSecondDigit));
	}

	if (mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("hourPlay");
	   $('ul.hourPlay li.before .inn').html(hhFirstDigit);
	   $('ul.hourPlay li.active .inn').html(setDigitHour(hhFirstDigit));
	}

	if (hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("hourTenths");
	   $('ul.hourTenths li.before .inn').html(hhSecondDigit);
	   $('ul.hourTenths li.active .inn').html(setDigitHourTenths(hhSecondDigit));
	}

	if (hhSecondDigit === 0 && hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("dayPlay");
	   $('ul.dayPlay li.before .inn').html(ddFirstDigit);
	   $('ul.dayPlay li.active .inn').html(setDigit(ddFirstDigit));
	}

	if (ddFirstDigit === 0 && hhSecondDigit === 0 && hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("dayTenths");
	   $('ul.dayTenths li.before .inn').html(ddSecondDigit);
	   $('ul.dayTenths li.active .inn').html(setDigitTenths(ddSecondDigit));
	}

}

function play(target) {
    $("body").removeClass("play");
    var aa = $("ul."+target+" li.active");

    if (aa.html() == undefined) {
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");

    }
    else if (aa.is(":last-child")) {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before").removeClass("active");
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("active")
            .closest("body")
            .addClass("play");
    }
    else {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");
    }

}

Basically each if statement is checking if all the digits to the right are 0. We can’t just check the digit next to it because we don’t want our hours to flip when we’re at 1 hour and 00 minutes and 59 seconds, we need to make sure its at 1 hour 00 minutes and 00 seconds. So some of these get pretty long.

That being said if you get this far you’re done. Your countdown clock should be flipping. If you have an issue explore the HTML source and CSS. Here’s the entire JS file I’m using.

function setDigit (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 9;
    }
}

function setDigitMin (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 5;
    }
}

function setDigitHour (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 3;
    }
}

function setDigitHourTenths (digit) {
    if (digit > 0) {
	    return digit-1;
    } else {
	    return 2;
    }
}

function getDate() {
	currentTime = new Date();

	countdownTime = Date.parse("April 1, 2013 21:10:00");

	countdownTotal = countdownTime - currentTime;

	// Set days, hours, mins, secs through the power of math	
	days  = Math.floor( countdownTotal / (1000*60*60*24) );
    hours = Math.floor( countdownTotal / (1000*60*60) );
    mins  = Math.floor( countdownTotal / (1000*60) );
    secs  = Math.floor( countdownTotal / 1000 );

    // Determine the number of days first. Hours = total time minus days. Etc
    dd = days;
    hh = hours - days * 24;
    mm = mins - hours * 60;
    ss = secs - mins * 60;

    // Set time. Going right to left for some reason. My bad.
    firstDigit = Math.floor(ss % 10);
    secondDigit = Math.floor(ss / 10);

    mmFirstDigit = Math.floor(mm % 10);
    mmSecondDigit = Math.floor(mm / 10);

    hhFirstDigit = Math.floor(hh % 10);
    hhSecondDigit = Math.floor(hh / 10);

    ddFirstDigit = Math.floor(dd % 10);
    ddSecondDigit = Math.floor(dd / 10);
}

function setTimer () {
	getDate();

	// If statement to fix glitch when loading in at 0 seconds in first spot
	if (firstDigit === 0) {
		$('ul.secondPlay li').eq(0).find('.inn').html(setDigit(firstDigit));
	} else {
	    $('ul.secondPlay li').eq(0).find('.inn').html(firstDigit);
    }

    if (firstDigit === 0) {
		$('ul.secondTenths li').eq(0).find('.inn').html(setDigitMin(secondDigit));
    } else {
	    $('ul.secondTenths li').eq(0).find('.inn').html(secondDigit);
    }

    if (secondDigit ===0 && firstDigit === 0) {
		$('ul.minutePlay li').eq(0).find('.inn').html(setDigit(mmFirstDigit));
    } else {
	    $('ul.minutePlay li').eq(0).find('.inn').html(mmFirstDigit);
    }

    if (mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.minuteTenths li').eq(0).find('.inn').html(setDigitMin(mmSecondDigit));
    } else {
	    $('ul.minuteTenths li').eq(0).find('.inn').html(mmSecondDigit);
    }

    if (mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.hourPlay li').eq(0).find('.inn').html(setDigitHour(hhFirstDigit));
    } else {
	    $('ul.hourPlay li').eq(0).find('.inn').html(hhFirstDigit);
    }

    if (hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.hourTenths li').eq(0).find('.inn').html(setDigitHourTenths(hhSecondDigit));
    } else {
	    $('ul.hourTenths li').eq(0).find('.inn').html(hhSecondDigit);
    }

    if (hhSecondDigit === 0 && hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.dayPlay li').eq(0).find('.inn').html(ddFirstDigit);
    } else {
	    $('ul.dayPlay li').eq(0).find('.inn').html(ddFirstDigit);
    }

    if (ddFirstDigit === 0 && hhSecondDigit === 0 && hhFirstDigit === 0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit ===0 && firstDigit === 0) {
		$('ul.dayTenths li').eq(0).find('.inn').html(setDigit(ddSecondDigit));
    } else {
	    $('ul.dayTenths li').eq(0).find('.inn').html(ddSecondDigit);
    }

    $('.container').css('display', 'block');				

    setInterval(function () {
        counter()
    }, 1000);

}

setTimer();

function counter () {
	getDate();

    //Calls secondPlay has to be before seconds are set
    play("secondPlay");
    $('ul.secondPlay li.before .inn').html(firstDigit);
	$('ul.secondPlay li.active .inn').html(setDigit(firstDigit));

    // Sets second second spot when first second spot === 0
	if (firstDigit === 0) {
	   play("secondTenths");
	   $('ul.secondTenths li.before .inn').html(secondDigit);
	   $('ul.secondTenths li.active .inn').html(setDigitMin(secondDigit));
	}

	if (secondDigit === 0 && firstDigit === 0) {
	   play("minutePlay");
	   $('ul.minutePlay li.before .inn').html(mmFirstDigit);
	   $('ul.minutePlay li.active .inn').html(setDigit(mmFirstDigit));
	}

	if (mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("minuteTenths");
	   $('ul.minuteTenths li.before .inn').html(mmSecondDigit);
	   $('ul.minuteTenths li.active .inn').html(setDigitMin(mmSecondDigit));
	}

	if (mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("hourPlay");
	   $('ul.hourPlay li.before .inn').html(hhFirstDigit);
	   $('ul.hourPlay li.active .inn').html(setDigitHour(hhFirstDigit));
	}

	if (hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("hourTenths");
	   $('ul.hourTenths li.before .inn').html(hhSecondDigit);
	   $('ul.hourTenths li.active .inn').html(setDigitHourTenths(hhSecondDigit));
	}

	if (hhSecondDigit === 0 && hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("dayPlay");
	   $('ul.dayPlay li.before .inn').html(ddFirstDigit);
	   $('ul.dayPlay li.active .inn').html(setDigit(ddFirstDigit));
	}

	if (ddFirstDigit === 0 && hhSecondDigit === 0 && hhFirstDigit ==0 && mmSecondDigit === 0 && mmFirstDigit === 0 && secondDigit === 0 && firstDigit === 0) {
	   play("dayTenths");
	   $('ul.dayTenths li.before .inn').html(ddSecondDigit);
	   $('ul.dayTenths li.active .inn').html(setDigitTenths(ddSecondDigit));
	}

}

function play(target) {
    $("body").removeClass("play");
    var aa = $("ul."+target+" li.active");

    if (aa.html() == undefined) {
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");

    }
    else if (aa.is(":last-child")) {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before").removeClass("active");
        aa = $("ul."+target+" li").eq(0);
        aa.addClass("active")
            .closest("body")
            .addClass("play");
    }
    else {
        $("ul."+target+" li").removeClass("before");
        aa.addClass("before")
            .removeClass("active")
            .next("li")
            .addClass("active")
            .closest("body")
            .addClass("play");
    }

}

That’s how I made a rotating clock with no images. All HTML, CSS3 and Javascript. Check out the demo.

Pin It on Pinterest