Search code examples
javascripthtml5-canvas

Why isn't the second hand showing on this clock? (HTML5 cavas layers with javascript)


I'm working on a clock animated using HTML5's canvas and javascript. It should have three moving hands, but only two seem to render and work (hour and minute). I don't see any runtime errors stopping the third one from being rendered. What's wrong with the third hand?

Example Clock BTW this is not an Earth clock so the math may look odd.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Wyrdling Clock</title>
</head>
<body onload="init();">
    <div style="position: relative;">
        <canvas id="clock_layer1" width="1080" height="1080" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
        <canvas id="clock_layer2" width="1080" height="1080" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
        <canvas id="clock_layer3" width="1080" height="1080" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
        <canvas id="clock_layer4" width="1080" height="1080" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
    </div>
</body>

<script>
    var clock_face = null,
        hour_hand = null,
        minute_hand = null,
        second_hand = null,
        cycle_hand = null,
        ctx_l1 = null,
        ctx_l2 = null,
        ctx_l3 = null,
        ctx_l4 = null,
        degrees = 0;
    var WyrdTimeStarts = new Date("2019-05-22");

    var HEIGHT = 1080;
    var WIDTH = 1080;
    var HALF_HEIGHT = HEIGHT / 2;
    var HALF_WIDTH = WIDTH / 2;
    var FPS = 15;

    var CIRCLE_TO_HOUR_RATIO = 360 / 9;
    var CIRCLE_TO_MINUTES_RATIO = 360 / 60;
    var CIRCLE_TO_SECOND_RATIO = 360 / 60;
    var CIRCLE_TO_CYCLE_RATIO = 360 / 3;
    var CIRCLE_TO_MS_RATIO = 360 / 60000;

    var DEGRESS_TO_RADIANS = Math.PI / 180;
    var RADIANS_TO_DEGREES = 180 / Math.PI;

    function fCleanCanvas() {
        // clear canvas
        ctx_l1.clearRect(0, 0, HEIGHT, WIDTH);
        ctx_l2.clearRect(0, 0, HEIGHT, WIDTH);
        ctx_l3.clearRect(0, 0, HEIGHT, WIDTH);
        ctx_l4.clearRect(0, 0, HEIGHT, WIDTH);
    } // End function fCleanCanvas

    function fMinuteAngle(currentMinutes) {
        // Calculate the expected angle
        return Math.floor(CIRCLE_TO_MINUTES_RATIO * currentMinutes, 0);
    } // End function fMinuteAngle

    function fHourAngle(currentHours) {
        // Calculate the expected angle
        return Math.floor(CIRCLE_TO_HOUR_RATIO * currentHours, 0);
    } // End function fHourAngle

    function fSecondAngle(currentTime) {
        // Calculate the expected angle
        var secondsToMilliseconds = currentTime.getSeconds() * 1000;
        var totalMilliseconds = currentTime.getMilliseconds() + secondsToMilliseconds;
        return Math.floor(CIRCLE_TO_MS_RATIO * totalMilliseconds, 0);
    } // End function fSecondAngle

    function fDayCycleAngle(currentHours) {
        // Calculate the expected angle
        return Math.floor(((360/3) * (currentHours/9)),0);
    } // End function fDayCycleAngle

    function fDrawAsRotatedInLayer(image, angle, x) {
        switch (x) {
            case 1:
                // Rotate around this point
                ctx_l1.rotate(angle * DEGRESS_TO_RADIANS);

                // Draw the image back and up
                ctx_l1.drawImage(image, 0 - HALF_HEIGHT, 0 - HALF_WIDTH);
    
                break;
            case 2:
                // Rotate around this point
                ctx_l2.rotate(angle * DEGRESS_TO_RADIANS);

                // Draw the image back and up
                ctx_l2.drawImage(image, 0 - HALF_HEIGHT, 0 - HALF_WIDTH);
    
                break;
            case 3:
                // Rotate around this point
                ctx_l3.rotate(angle * DEGRESS_TO_RADIANS);

                // Draw the image back and up
                ctx_l3.drawImage(image, 0 - HALF_HEIGHT, 0 - HALF_WIDTH);
    
                break;
            case 3:
                // Rotate around this point
                ctx_l4.rotate(angle * DEGRESS_TO_RADIANS);

                // Draw the image back and up
                ctx_l4.drawImage(image, 0 - HALF_HEIGHT, 0 - HALF_WIDTH);
    
                break;
            default:
                break;
        }
    } // End function fDrawAsRotatedInLayer

    function draw() {
        var currentTime = new Date();
        var today = new Date();
        var diffMs = (today - WyrdTimeStarts); // milliseconds in the Wyrd so far
        var diffDays = Math.floor(diffMs / 97200000); // Wyrd days
        var diffHrs = (diffMs % 97200000) / 3600000; // hours
        var diffMins = ((diffMs % 97200000) % 3600000) / 60000; // minutes

        fCleanCanvas();

        // Draw the clock background onto canvas layer 1
        ctx_l1.drawImage(clock_face_base, 0, 0);

        // Save canvas layer 1's drawing state
        ctx_l1.save();

        // Move to center point for rotation of hour hand
        ctx_l1.translate(HALF_HEIGHT, HALF_WIDTH);
        fDrawAsRotatedInLayer(hour_hand, fHourAngle(diffHrs), 1);

        // Restore canvas layer 1's previous drawing state
        ctx_l1.restore();



        // Draw the clock's middle layer onto canvas layer 2
        ctx_l2.drawImage(clock_face_mid, 0, 0);

        // Save canvas layer 2's drawing state
        ctx_l2.save();

        // Move to center point for rotation of minute hand
        ctx_l2.translate(HALF_HEIGHT, HALF_WIDTH);
        fDrawAsRotatedInLayer(minute_hand, fMinuteAngle(diffMins), 2);
        
        // Restore canvas layer 2's previous drawing state
        ctx_l2.restore();



        // Save canvas layer 3's drawing state
        ctx_l3.save();

        // Move to center point for rotation of day cycle (shows which 9 hour segment of the 27 hour day is it)
        ctx_l3.translate(HALF_HEIGHT, HALF_WIDTH);
        fDrawAsRotatedInLayer(cycle_hand, fDayCycleAngle(diffHrs), 3);

        // Restore canvas layer 3's previous drawing state
        ctx_l3.restore();



        // Draw the clock's cap onto canvas layer 4
        ctx_l4.drawImage(clock_face_top, 0, 0);

        // Save canvas layer 4's drawing state
        ctx_l4.save();

        // Move to center point for rotation of second hand
        ctx_l4.translate(HALF_HEIGHT, HALF_WIDTH);
        fDrawAsRotatedInLayer(second_hand, fSecondAngle(currentTime), 4);

        // Restore canvas layer 4's previous drawing state
        ctx_l4.restore();



        window.requestAnimationFrame(draw, 1000 / FPS);
        
    } // End function draw

    function imgLoaded() {
        // Image loaded event complete.  Start the timer
        window.requestAnimationFrame(draw, 1000 / FPS);
    } // End function imgLoaded

    function init() {
        // Grab the clock element
        var canvas1 = document.getElementById('clock_layer1');
        var canvas2 = document.getElementById('clock_layer2');
        var canvas3 = document.getElementById('clock_layer3');
        var canvas4 = document.getElementById('clock_layer4');
    
        // Canvas supported?
        if(canvas1.getContext('2d')) {
            ctx_l1 = canvas1.getContext('2d');
            ctx_l2 = canvas2.getContext('2d');
            ctx_l3 = canvas3.getContext('2d');
            ctx_l4 = canvas4.getContext('2d');
    
            // Load the hour hand image
            hour_hand = new Image();
            hour_hand.src = 'Wyrdling Clock - hand 1.png';
    
            // Load the minute hand image
            minute_hand = new Image();
            minute_hand.src = 'Wyrdling Clock - hand 2.png';
    
            // Load the cycle hand image
            cycle_hand = new Image();
            cycle_hand.src = 'Wyrdling Clock - hand 3.png';
    
            // Load the second hand image
            second_hand = new Image();
            second_hand.src = 'Wyrdling Clock - hand 4.png';
    
            // Load the clock face part 2 image
            clock_face_mid = new Image();
            clock_face_mid.src = 'Wyrdling Clock - face 2.png';

            // Load the clock face part 3 image
            clock_face_top = new Image();
            clock_face_top.src = 'Wyrdling Clock - face 3.png';

            // Load the clock face base image
            clock_face_base = new Image();
            clock_face_base.src = 'Wyrdling Clock - face 1.png';
            clock_face_base.onload = imgLoaded;

        } else {
            alert("Canvas not supported!");
        } // End if(canvas1.getContext('2d'))

    } // End function init

</script>
</html>

Solution

  • Once you see it, you are going to kick yourself for this.
    Look at the switch in fDrawAsRotatedInLayer you have 2 times case 3:. It never enters the branch for the seconds handle