Skip to content Skip to sidebar Skip to footer

Dynamically Changing CSS Keyframe Values To Create A Clock

This is my 'very first' foray into keyframe animation. I was put off before by the alleged inability to dynamically change hard coded CSS values on the fly via JavaScript. I Googl

Solution 1:

I don't really like the idea of inserting all those keyframes dynamically with unique names. If I were doing this and trying to use CSS keyframe animations, I'd create two animations, one that provides your "double-bounce" effect, and one that rotates a wrapper around the center.

Using just HTML and CSS, you can almost create a full working clock, although you don't have any way to start it at the right time without JavaScript.

This was a fun exercise though, and I was able to recreate the whole clock effect using just keyframe animations with a tiny bit of JavaScript to determine the starting time of the animation and supply a negative animation-delay to offset the animations to the proper timeframes.

The time can wander a bit, but really not as much as you might think. I left the animations running overnight in Chrome, FF, and IE, and allowed my computer to go to sleep, and 9 hours later the clocks were still keeping perfect time. However, when I left it running in a background tab of my phone overnight, the clock was completely broken in the morning. Though, seeing as how the whole purpose is to display a clock on a webpage, it's unlikely that the page would stick around long enough for the clock to be noticeably off.

Anyway, here's the snippet:

window.addEventListener("load", function() {
  var now = new Date();
  var secondsDelay = now.getSeconds();
  var minutesDelay = now.getMinutes() * 60 + secondsDelay;
  var hoursDelay = (now.getHours() % 12) * 3600 + minutesDelay;
  var minuteHand = document.querySelector(".minute .hand");
  var minuteWrapper = document.querySelector(".minute");
  var secondWrapper = document.querySelector(".second");
  var hourWrapper = document.querySelector(".hour");

  //set animation offsets with negative delay
  minuteHand.style.animation = "minuteTick 60s -" + secondsDelay + "s infinite";
  secondWrapper.style.animation = "rotateHolder steps(60) 60s -" + secondsDelay + "s infinite";
  minuteWrapper.style.animation = "rotateHolder steps(60) 3600s -" + minutesDelay + "s infinite";
  hourWrapper.style.animation = "rotateHolder steps(43200) 43200s -" + hoursDelay + "s infinite";

  //start running
  document.querySelector(".clock").classList.add("running");
});
.clock {
  width: 200px;
  height: 200px;
  background-color: gray;
  border-radius: 50%;
}
.sectionWrapper {
  width: 200px;
  height: 200px;
  position: absolute;
  transform: rotate(0deg);
}
.clock.running .second {
  animation: rotateHolder 60s steps(60) infinite;
}
.clock.running .second .hand {
  animation: secondTick 1s 0s infinite;
}
.clock.running .minute {
  animation: rotateHolder 3600s steps(60) infinite;
}
.clock.running .minute .hand {
  animation: minuteTick 60s 0s infinite;
}
.clock.running .hour {
  animation: rotateHolder 43200s steps(43200) infinite;
}
.hand {
  display: block;
  position: absolute;
  background-color: #000;
  transform-origin: center bottom;
}
.second .hand {
  left: 99px;
  top: 5px;
  height: 95px;
  width: 2px;
  background-color: red;
}
.minute .hand {
  left: 98px;
  top: 15px;
  height: 85px;
  width: 4px;
}
.hour .hand {
  left: 97px;
  top: 45px;
  height: 55px;
  width: 6px;
}
@keyframes secondTick {
  0% { transform: rotate(0deg); animation-timing-function: ease-in; }
  25% { transform: rotate(6deg); animation-timing-function: ease-out; }
  45% { transform: rotate(4deg); animation-timing-function: ease-in; }
  65% { transform: rotate(6deg); animation-timing-function: ease-out; } 
  75% { transform: rotate(5deg); animation-timing-function: ease-in; }
  85%,100% { transform: rotate(6deg);animation-timing-function: ease-out; }				
}
@keyframes minuteTick {
  0%,98.3% { transform: rotate(0deg); animation-timing-function: ease-in; }
  98.8% { transform: rotate(6deg); animation-timing-function: ease-out; }
  99.1% { transform: rotate(4deg); animation-timing-function: ease-in; }
  99.4% { transform: rotate(6deg); animation-timing-function: ease-out; } 
  99.6% { transform: rotate(5deg); animation-timing-function: ease-in; }
  99.8%,100% { transform: rotate(6deg);animation-timing-function: ease-out; }				
}
@keyframes rotateHolder {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
<div class="clock">
  <div class="hour sectionWrapper">
    <div class="hand"></div>
  </div>
  <div class="minute sectionWrapper">
    <div class="hand"></div>
  </div>
  <div class="second sectionWrapper">
    <div class="hand"></div>
  </div>
</div>

Solution 2:

Finished clock.

(function () {

    /* Station Style Clock - kurt.grigg@yahoo.co.uk */

    /* ^^^^^^^^^^^^^^^ Config below ^^^^^^^^^^^^^^^ */

    var clockSize = 400;
    var clockColour = 'rgb(255,255,255)';
    var secHandColour = 'rgb(255,255,255)';

    /* ^^^^^^^^^^^^^^^^^ End config ^^^^^^^^^^^^^^^^ */

    var d = document;
    var mcon = [];
    var mrkrs = [];
    var e = 360/60;
    var degr = -6;
    var mls = 100;
    var prev = performance.now();
    var radi = Math.PI / 180;
    var offs = 90 * radi;
    var rndId = 'id'+Math.random() * 1;
    var secSpan = '.8s';
    var minSpan = '1s';
    var houSpan = '1s';
    var secIncr = 0;
    var minIncr = 0;
    var houIncr = 0;
    var secDeg, minDeg, houDeg, preSec, preMin, preHou;
    var idx = d.getElementsByTagName('div').length;
    var shdcol = 'rgba(0,0,0,0.6)';
    var eiatf = ' translateZ(0); animation-timing-function: ease-in';
    var eoatf = ' translateZ(0); animation-timing-function: ease-out';

    d.write('<div id = "'+rndId+'" style="display:inline-block;line-height:0px;"></div>');

    function xy (a) {
        return (a * (clockSize * 2) / 100);
    }

    function shdw(s, h) {
        var depth = xy(h);
        var angle = s * radi;
        var vsa = depth * Math.cos(angle);
        var hsa = depth * Math.sin(angle);
        return {vsa:vsa, hsa:hsa}
    }

    var dial = d.createElement('div');
    dial.setAttribute('style', 'display: inline-block;'
        +'position: relative;'
        +'height: '+xy(100)+'px;'
        +'width: '+xy(100)+'px;'
        +'background-color:transparent;'
        +'border-radius: 50%;'
        +'margin: -'+xy(24)+'px -'+xy(24)+'px -'+xy(24)+'px -'+xy(24)+'px;');

    d.getElementById(rndId).style.transform = 'scale3d(.5,.5,1)';
    d.getElementById(rndId).appendChild(dial); 

    for (var i = 0; i < 60; i++) {

        var mky = shdw(i * 6, 0.5).vsa;
        var mkx = shdw(i * 6, 0.5).hsa;

        var len = (i % 5) ? 4 : 8;
        var wid = (i % 5) ? .8 : 4;

        mcon[i] = d.createElement('div');
        mcon[i].setAttribute('style', 'display: block;'
            +'position: absolute;'
            +'width: '+xy(4)+'px;'
            +'height: '+xy(8)+'px;'
            +'margin: auto; top: 0;bottom: 0;left: 0;right: 0;'
            +'font-size: 0px;line-height: 0px;padding: 0;'
            +'color: '+clockColour+';');

        mrkrs[i] = d.createElement('div');
        mrkrs[i].setAttribute('style', 'display: block;'
            +'position: absolute;'
            +'width: '+xy(wid)+'px;'
            +'height: '+xy(len)+'px;'
            +'margin: auto; top: 0;left: 0;right: 0;'
            +'font-size: 0px;line-height: 0px;padding: 0;'
            +'box-shadow:'+mkx+'px '+mky+'px 0px 0px rgba(0,0,0,0.7);'
            +'background-color:'+clockColour+';');
        mcon[i].appendChild(mrkrs[i]);
        dial.appendChild(mcon[i]);
        degr += 6;
        mcon[i].style.top = xy(0) + xy(86) * Math.sin(-offs + e * i * radi) + 'px';
        mcon[i].style.left= xy(0) + xy(86) * Math.cos(-offs + e * i * radi) + 'px';
        mcon[i].style.transform = 'rotate(' + (degr) + 'deg)';
        mcon[i].style.transformOrigin = 'center center';
    }

    /* Generic container div for all hands */

    var handContainers = 'display: block;'
        +'position: absolute;'
        +'height: '+xy(100)+'px;'
        +'width: '+xy(16)+'px;'
        +'font-size: 0px; line-height: 0px; padding: 0;'
        +'margin: auto; top: 0;bottom: 0; left: 0; right: 0;'
        +'transform-origin: center center;';
    

    /* Hour hand CSS */

    var houClone = handContainers;
    var houHand = d.createElement('div');
    houHand.setAttribute('style', houClone);
    
    dial.appendChild(houHand);

    var hh = d.createElement('div');
    hh.setAttribute('style', 'display: block;'
        +'position: absolute;'
        +'height: '+xy(48)+'px;'
        +'width: '+xy(5)+'px;'
        +'top: '+xy(15)+'px;'
        +'margin: auto; left: 0; right: 0;'
        +'outline: 1px solid transparent;'
        +'background-color: '+clockColour+';');
    houHand.appendChild(hh);

    var houShad = houHand.cloneNode(true);
    houShad.childNodes[0].style.backgroundColor = shdcol;

    /* Minute hand CSS */

    var minClone = handContainers;
    var minHand = d.createElement('div');
    minHand.setAttribute('style', minClone);

    dial.appendChild(minHand);

    var mh = d.createElement('div');
    mh.setAttribute('style', 'display:block;'
        +'position: absolute;'
        +'height: '+xy(58)+'px;'
        +'width: '+xy(3)+'px;'
        +'top: '+xy(5)+'px;'
        +'margin: auto; left: 0; right: 0;'
        +'outline: 1px solid transparent;'
        +'background-color: '+clockColour+';');
    minHand.appendChild(mh);

    var minShad = minHand.cloneNode(true);
    minShad.childNodes[0].style.backgroundColor = shdcol;

    /* Seconds hand CSS */

    var secClone = handContainers;
    var secHand = d.createElement('div');
    secHand.setAttribute('style', secClone);
    dial.appendChild(secHand);

    var sh = d.createElement('div');
    var svgsec = '<svg height="'+xy(100)+'" width="'+xy(16)+'">'
    +'<g>'
    +'<rect id="hd" x="'+xy(7.45)+'" y="'+xy(3)+'" width="'+xy(1)+'" height="'+xy(60)+'" stroke-width="0" fill="'+secHandColour+'"/>'
    +'<circle id="cw" cx="50%" cy="'+xy(67)+'" r="'+xy(4)+'" stroke-width="0" stroke="'+secHandColour+'" fill="'+secHandColour+'"/>'
    +'</g>'
    +'</svg>';
    sh.innerHTML = svgsec;
    secHand.appendChild(sh);

    var secShad = secHand.cloneNode(true);
    var newColor = '"'+shdcol+'"';
    var clnshd = svgsec.split(secHandColour).join("");
    clnshd = clnshd.replace(/""/g, newColor);
    secShad.innerHTML = clnshd;

    var nut = d.createElement('div');
    nut.setAttribute('style', 'display: block;'
        +'position: absolute;'
        +'height: '+xy(4)+'px;'
        +'width: '+xy(4)+'px;'
        +'border-radius: 50%;'
        +'margin: auto;top: 0;bottom: 0;left: 0;right: 0;'
        +'background-color: '+secHandColour+';'
        +'box-shadow:'+xy(0)+'px '+xy(0.5)+'px 0px 0px rgba(0,0,0,0.7);'
        +'z-index: 105;');
    dial.appendChild(nut);

    function houKeyFrames() {
        var houSheet = (d.getElementById('tmphouSheet'+idx));
        if (houSheet) {
            houSheet.parentNode.removeChild(houSheet);
        }

        houClone = handContainers;
 
        var p1 = houDeg;
        var p2 = houDeg+1;
        var p3 = houDeg+0.4;
        var p4 = houDeg+1;
        var p5 = houDeg+0.5; 
        var p6 = houDeg+1; 

        var houframes = '@keyframes hou'+idx+'gen'+houIncr+' { '
        +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
        +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
        +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
        +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
        +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
        +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

        var hs = document.createElement( 'style' );
        hs.setAttribute('id', 'tmphouSheet'+idx);
        hs.innerHTML = houframes;
        d.getElementsByTagName('head')[0].appendChild(hs);

        var houAni = 'animation: hou'+idx+'gen'+houIncr+' '+houSpan+' 1 forwards;';
        houClone += houAni;
        houHand.setAttribute('style', houClone);
        houHand.style.zIndex = 100;
        dial.appendChild(houHand);

        houShad.setAttribute('style', houClone);
        houShad.style.top = xy(2.5)+'px';
        houShad.style.zIndex = 99;
        dial.appendChild(houShad);
    }

    function minKeyFrames() {
        var minSheet = (d.getElementById('tmpMinSheet'+idx));
        if (minSheet) {
            minSheet.parentNode.removeChild(minSheet);
        }

        minClone = handContainers;

        var p1 = minDeg;
        var p2 = minDeg+6;
        var p3 = minDeg+4;
        var p4 = minDeg+6;
        var p5 = minDeg+5; 
        var p6 = minDeg+6;

        var minframes = '@keyframes min'+idx+'gen'+minIncr+' { '
        +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
        +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
        +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
        +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
        +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
        +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

        var ms = document.createElement( 'style' );
        ms.setAttribute('id', 'tmpMinSheet'+idx);
        ms.innerHTML = minframes;
        d.getElementsByTagName('head')[0].appendChild(ms);

        var minAni = 'animation: min'+idx+'gen'+minIncr+' '+minSpan+' 1 forwards;';
        minClone += minAni;
        minHand.setAttribute('style', minClone);
        minHand.style.zIndex = 102;
        dial.appendChild(minHand);

        minShad.setAttribute('style', minClone);
        minShad.style.top = xy(3)+'px';
        minShad.style.zIndex = 101;
        dial.appendChild(minShad);
    }

    function secKeyFrames() {
        var secSheet = (d.getElementById('tmpSecSheet'+idx));
        if (secSheet) {
            secSheet.parentNode.removeChild(secSheet);
        }
        
        secClone = handContainers;
 
        var p1 = secDeg;
        var p2 = secDeg+6;
        var p3 = secDeg+4;
        var p4 = secDeg+6;
        var p5 = secDeg+5; 
        var p6 = secDeg+6; 

        var secframes = '@keyframes sec'+idx+'gen'+secIncr+' { '
        +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
        +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
        +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
        +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
        +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
        +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

        var ss = document.createElement( 'style' );
        ss.setAttribute('id', 'tmpSecSheet'+idx);
        ss.innerHTML = secframes;
        document.getElementsByTagName('head')[0].appendChild(ss);

        var secAni = 'animation: sec'+idx+'gen'+secIncr+' '+secSpan+' 1 forwards;';
        secClone += secAni;
        secHand.setAttribute('style', secClone);
        secHand.style.zIndex = 104;
        dial.appendChild(secHand);

        secShad.setAttribute('style', secClone);
        secShad.style.top = xy(3.5)+'px';
        secShad.style.zIndex = 103;
        dial.appendChild(secShad);
    }

    function clock() {
        var x = new Date();
        var seconds = x.getSeconds();
        var minutes = x.getMinutes();
        var hours = (x.getHours() * 30) + (x.getMinutes() / 2);
        
        if (seconds !== preSec) {
            secIncr++;
            secDeg = (seconds-1) * 6;
            secHand.removeAttribute('style');
            secKeyFrames();
            if (secIncr > 59) {
                secIncr = 0;
            }
        }

        if (minutes !== preMin) {
            minIncr++;
            minDeg = (minutes-1) * 6;
            minHand.removeAttribute('style');
            minKeyFrames();
            if (minIncr > 59) {
                minIncr = 0;
            }
        }

        if (hours !== preHou) {
            houIncr++;
            houDeg = (hours-1) * 1;
            houHand.removeAttribute('style');
            houKeyFrames();
            if (houIncr > 59) {
                houIncr = 0;
            }  
        }

        preSec = seconds;
        preMin = minutes;
        preHou = hours;
    }

    function cyc() {
        var pres = performance.now(); 
        if ((pres - prev) > mls) {
            clock();
            prev = performance.now();
        }
        window.requestAnimationFrame(cyc);
    } 

    window.addEventListener('load', cyc, false);
})();
<!doctype html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Clock</title>

    <style type="text/css">
        html {
            height: 100%;
        }
        
        body {
            background-color: #ffff00;
            text-align: center;
        }
    </style>
</head>

<body>
  
  
  
</body>
</html>  

Solution 3:

This seems way too complicated. Whenever I'm working with complex animations, I use GSAP. Here's a simplied demo that I made. It uses basic JQuery and the GSAP animation engine, TweenMax.

You can easily adjust the easing with GSAP via their ease visualizer

$(document).ready(function() {
  var clock = $('.clock'); // clock container
  var arm = $('.arm'); // seconds arm
  var log = $('.log'); // log beneath clock
  var position =  90; // starting position at 0 seconds
  var time = 0; 

setInterval(function(){
  position += 6; // 360 / 60 = +6 deg per second
  time ++; 
  TweenLite.to(arm, 0.3, {rotation:position, transformOrigin:"right bottom", ease: Back.easeOut.config(2), y: 0 });
  log.text(time + " seconds and " + position +  " degs");
 }, 1000);

});

Solution 4:

Focusing just in the code, I find it quite good.

my only concern would be

/* Put it all together and it's ready to go */ 
var newAni = 'animation: reGen'+incr+' 1s 1 forwards;';
coreCss += newAni;

Note that coreCss will at the end have lots of animations (verified in dev tools)

You should just clear it.

But given that the animation is setting the position via forwards, may be that can give problems ...

If this is the case, may be playing with just 2 animations ??

In case that you want advice about how I would get that effect, I would set 2 nested elements, one with the elaborate keyframes that you have , and running every second. And the other making the full circle, stepped 60 times ...


Solution 5:

BBC clock example for Dave.

(function () {

/* The BBC Analogue Clock - kurt.grigg@yahoo.co.uk */

/* ^^^^^^^^^^^^^^^^^^^ Config below ^^^^^^^^^^^^^^^^^^^ */

var clockSize = 300;
var dialcol = 'rgba(0,0,255,0.9)';
var handcol = 'rgb(230,230,230)';

/* ^^^^^^^^^^^^^^^^^^^ End config ^^^^^^^^^^^^^^^^^^^ */

var d = document;
var mrkrs = [];
var e = (360 / 12);
var degr = 0;
var mls = 100;
var prev = performance.now();
var radi = Math.PI / 180;
var offs = 60 * radi;
var rndId = 'id'+Math.random() * 1;
var sSpan = '.9s';
var mSpan = '1s';
var hSpan = '1s';
var sIncr = 0;
var mIncr = 0;
var hIncr = 0;
var sDeg, mDeg, hDeg, sPre, mPre, hPre;
var idx = d.getElementsByTagName('div').length;
d.write('<div id = "'+rndId+'" style="display:inline-block;line-height:0px;"></div>');

function xy(a) {
    return (a * clockSize / 100);
}

var dial = d.createElement('div');
dial.setAttribute('style', 'display: inline-block;'
    +'position: relative;'
    +'height: '+clockSize+'px;'
    +'width: '+clockSize+'px;'
    +'margin: 0px;padding: 0px;'
    +'border-radius: 5%;z-index: 1;'
    +'background-color: '+dialcol+';');
d.getElementById(rndId).appendChild(dial);

for (var i = 0; i < 12; i++) {

    var incr = xy(2.0) + (i * xy(0.2));

    mrkrs[i] = d.createElement('div');
    mrkrs[i].setAttribute('style', 'display: block;'
        +'position: absolute;'
        +'width: '+xy(14)+'px;'
        +'height: '+xy(14)+'px;'
        +'margin: auto; top: 0;bottom: 0;left: 0;right: 0;'
        +'font-size: 0px;line-height: 0px;padding: 0;'
        +'text-align: center !important;'
        +'background-color: transparent;');

    mrkrs[i].innerHTML = '<div style = "display: inline-block;'
    +'position: relative;width: '+incr+'px;height: '+xy(14)+'px;'
    +'font-size: 0px;background-color:'+handcol+';'
    +'margin-right: '+xy(0.6)+'px;"></div>'
    +'<div style = "display:inline-block;position: relative;'
    +'width: '+incr+'px;height: '+xy(14)+'px;font-size: 0px;'
    +'margin-left: '+xy(0.6)+'px;'
    +'background-color:'+handcol+';"></div>';

    dial.appendChild(mrkrs[i]);
    degr += 30;

    mrkrs[i].style.top = xy(0) + xy(77) * 
        Math.sin(-offs + e * i * radi) + 'px';
    mrkrs[i].style.left= xy(0) + xy(77) * 
        Math.cos(-offs + e * i * radi) + 'px';
    mrkrs[i].style.transform = 'rotate(' + (degr) + 'deg)';
    mrkrs[i].style.transformOrigin = 'center center';
}

/* Hour CSS */

var hCss = 'display: block;'
    +'position: absolute;'
    +'height: '+xy(56)+'px;'
    +'width: '+xy(6)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0;bottom: 0; left: 0; right: 0;'
    +'transform-origin: center center 0;'
    +'z-index: 2;';
var hClone = hCss;

var houHand = d.createElement('div');
houHand.setAttribute('style', hClone);
dial.appendChild(houHand);

var hh = d.createElement('div');
hh.setAttribute('style', 'display: block;'
    +'position: absolute;'
    +'height: '+xy(21)+'px;'
    +'width: '+xy(6)+'px;' 
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0; left: 0; right: 0;'
    +'background-color: '+handcol+';');
houHand.appendChild(hh);

/* Minute CSS */

var mCss = 'display: block;'
    +'position: absolute;'
    +'height: '+xy(86)+'px;'
    +'width: '+xy(4)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0; bottom: 0; left: 0; right: 0;'
    +'transform-origin: center center;'
    +'z-index: 3;';
var mClone = mCss;

var minHand = d.createElement('div');
minHand.setAttribute('style', mClone);
dial.appendChild(minHand);

var mh = d.createElement('div');
mh.setAttribute('style', 'display: block;'
    +'position: absolute;'
    +'height: '+xy(36)+'px;'
    +'width: '+xy(4)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0; left: 0; right: 0;'
    +'background-color: '+handcol+';');
minHand.appendChild(mh);

/* Second CSS */

var sCss = 'display: block;'
    +'position: absolute;'
    +'height: '+xy(90)+'px;'
    +'width: '+xy(2)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0;bottom: 0; left: 0; right: 0;'
    +'transform-origin: center center;'
    +'z-index: 4;';
var sClone = sCss;

var secHand = d.createElement('div');
secHand.setAttribute('style', sClone);
dial.appendChild(secHand);

var sh = d.createElement('div');
sh.setAttribute('style', 'display: block;'
    +'position: absolute;'
    +'height: '+xy(39)+'px;'
    +'width: '+xy(2)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'margin: auto; top: 0; left: 0; right: 0;'
    +'background-color: '+handcol+';');
secHand.appendChild(sh);

var sectail = d.createElement('div');
sectail.setAttribute('style', 'display: block;'
    +'position: absolute;'
    +'height: '+xy(12)+'px;'
    +'width: '+xy(2)+'px;'
    +'margin: auto; left: 0; right: 0;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'top: '+xy(52)+'px;'
    +'background-color: '+handcol+';');
secHand.appendChild(sectail);

/* Centre nut & optional glass front CSS */

var nut = d.createElement('div');
nut.setAttribute('style', 'display: inline-block;'
    +'position: absolute;'
    +'height: '+xy(10)+'px;'
    +'width: '+xy(10)+'px;'
    +'font-size: 0px;line-height: 0px;padding: 0;'
    +'border: '+xy(3)+'px solid '+handcol+';'
    +'border-radius: 50%;'
    +'margin: auto;top: 0;bottom: 0;left: 0;right: 0;'
    +'background-color: transparent;'
    +'z-index: 5;');
dial.appendChild(nut);

var glass = d.createElement('div');
glass.setAttribute('style', 'display: block;'
    +'height:'+clockSize+'px;'
    +'width: '+clockSize+'px;'
    +'border-radius:'+xy(5)+'px;'
    +'margin: auto;top: 0;bottom: 0;left: 0;right: 0;'
    +'z-index: 6;box-shadow:'
    +'0 '+xy(1)+'px '+xy(1)+'px rgba(0,0,0,0.5),'
    +'inset 0 '+xy(1)+'px rgba(255,255,255,0.3),'
    +'inset 0 '+xy(10)+'px rgba(255,255,255,0.2),'
    +'inset 0 '+xy(10)+'px '+xy(20)+'px rgba(255,255,255,0.25),'
    +'inset 0 -'+xy(15)+'px '+xy(30)+'px rgba(0,0,0,0.3);');
dial.appendChild(glass);

var eiatf = 'translateZ(0); animation-timing-function: ease-in';
var eoatf = 'translateZ(0); animation-timing-function: ease-out';

function secKeyFrames() {
    var secSheet = (d.getElementById('tmpSecSheet'+idx));
    if (secSheet) {
        secSheet.outerHTML = '';
    }
    
    sClone = sCss;
 
    var p1 = sDeg;
    var p2 = sDeg+6;
    var p3 = sDeg+4;
    var p4 = sDeg+6;
    var p5 = sDeg+5; 
    var p6 = sDeg+6; 

    var secframes = '@keyframes reGen'+sIncr+' { '
    +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
    +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
    +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
    +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
    +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
    +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

    var ss = document.createElement( 'style' );
    ss.setAttribute('id', 'tmpSecSheet'+idx);
    ss.innerHTML = secframes;
    document.getElementsByTagName('head')[0].appendChild(ss);

    var secAni = 'animation: reGen'+sIncr+' '+sSpan+' 1 forwards;';
    sClone += secAni;
    secHand.setAttribute('style', sClone);
    dial.appendChild(secHand);
}

function minKeyFrames() {
    var minSheet = (d.getElementById('tmpMinSheet'+idx));
    if (minSheet) {
        minSheet.outerHTML = '';
    }

    mClone = mCss;
 
    var p1 = mDeg;
    var p2 = mDeg+6;
    var p3 = mDeg+4;
    var p4 = mDeg+6;
    var p5 = mDeg+5; 
    var p6 = mDeg+6;

    var minframes = '@keyframes minGen'+mIncr+' { '
    +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
    +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
    +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
    +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
    +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
    +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

    var ms = document.createElement( 'style' );
    ms.setAttribute('id', 'tmpMinSheet'+idx);
    ms.innerHTML = minframes;
    d.getElementsByTagName('head')[0].appendChild(ms);

    var minAni = 'animation: minGen'+mIncr+' '+mSpan+' 1 forwards;';
    mClone += minAni;
    minHand.setAttribute('style', mClone);
    dial.appendChild(minHand);
}

function houKeyFrames() {
    var houSheet = (d.getElementById('tmphouSheet'+idx));
    if (houSheet) {
        houSheet.outerHTML = '';
    }

    hClone = hCss;
 
    var p1 = hDeg;
    var p2 = hDeg+1;
    var p3 = hDeg+0.4;
    var p4 = hDeg+1;
    var p5 = hDeg+0.5; 
    var p6 = hDeg+1; 

    var houframes = '@keyframes houGen'+hIncr+' { '
    +'0% { transform: rotate('+p1+'deg) '+eiatf+';}'
    +'30% { transform: rotate('+p2+'deg) '+eoatf+';}'
    +'45% { transform: rotate('+p3+'deg) '+eiatf+';}'
    +'60% { transform: rotate('+p4+'deg) '+eoatf+';}' 
    +'70% { transform: rotate('+p5+'deg) '+eiatf+';}'
    +'80%,100% { transform: rotate('+p6+'deg) '+eoatf+';}}';

    var hs = document.createElement( 'style' );
    hs.setAttribute('id', 'tmphouSheet'+idx);
    hs.innerHTML = houframes;
    d.getElementsByTagName('head')[0].appendChild(hs);

    var houAni = 'animation: houGen'+hIncr+' '+hSpan+' 1 forwards;';
    hClone += houAni;
    houHand.setAttribute('style', hClone);
    dial.appendChild(houHand);
}

function animate() {
    var x = new Date();
    var seconds = x.getSeconds();
    var minutes = x.getMinutes();
    var hours = (x.getHours() * 30) + (x.getMinutes() / 2);
    
    if (seconds !== sPre) {
        sIncr++;
        sDeg = (seconds-1) * 6;
        secHand.removeAttribute('style');
        secKeyFrames();
        if (sIncr > 59) {
            sIncr = 0;
        }
    }

    if (minutes !== mPre) {
        mIncr++;
        mDeg = (minutes-1) * 6;
        minHand.removeAttribute('style');
        minKeyFrames();
        if (mIncr > 59) {
            mIncr = 0;
        }
    }

    if (hours !== hPre) {
        hIncr++;
        hDeg = (hours-1) * 1;
        houHand.removeAttribute('style');
        houKeyFrames();
        if (hIncr > 59) {
            hIncr = 0;
        }    
    }

    sPre = seconds;
    mPre = minutes;
    hPre = hours;
}

function cyc() {
    var pres = performance.now(); 
    if ((pres - prev) > mls) {
        animate();
        prev = performance.now();
    }
    window.requestAnimationFrame(cyc);
} 

window.addEventListener('load', cyc, false);
})();

Post a Comment for "Dynamically Changing CSS Keyframe Values To Create A Clock"