Skip to content

Instantly share code, notes, and snippets.

@agehrke
Created December 18, 2016 11:07
Show Gist options
  • Save agehrke/c9b180ad053fc8ca988c93947c996cb2 to your computer and use it in GitHub Desktop.
Save agehrke/c9b180ad053fc8ca988c93947c996cb2 to your computer and use it in GitHub Desktop.
Retina-ready countdown timer
<div id="canvastimer"
class="count-down"
data-timeend="Fri Dec 31 2016 23:59:59 GMT+0100 (CET)
">
</div>
<div id="canvastimer1"
class="count-down"
data-timeend="Fri Dec 30 2016 23:59:59 GMT+0100 (CET)
">
</div>
function createRinger() {
// This needs to be defined to allow scoping. Otherwise it would be a global variable used by all instances of ringer.
// I would recommend to rewrite the ringer object using a more object-oriented approach,
// e.g. class or similar where all state is encapsulated inside object itself, and not relay on javascript closures and scoping for the $r varible to work.
var $r = null;
return {
countdown_to:"",
rings: {
'Dage': {
s: 86400000, // mseconds in a day,
max: 14
},
'Timer': {
s: 3600000, // mseconds per hour,
max: 24
},
'Minutter': {
s: 60000, // mseconds per minute
max: 60
},
'Sekunder': {
s: 1000,
max: 60
},
'MICROSEC': {
s: 10,
max: 100
}
},
r_count: 4,
r_spacing: 30, // px
r_size: 140, // px
r_thickness: 4, // px
update_interval: 1000, // ms
init: function(element){
$r = this;
$r.countdown_to = $(element).data("timeend");
$r.cvs = document.createElement('canvas');
$r.size = {
w: ($r.r_size + $r.r_thickness) * $r.r_count + ($r.r_spacing*($r.r_count-1)),
h: ($r.r_size + $r.r_thickness)
};
//added devicePixelRatio for retina screens
$r.cvs.setAttribute('width',$r.size.w * window.devicePixelRatio);
$r.cvs.setAttribute('height',$r.size.h * window.devicePixelRatio);
$r.ctx = $r.cvs.getContext('2d');
//*1 multiply for non-retinas
$r.ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
$(element).append($r.cvs);
$r.cvs = $($r.cvs);
$r.ctx.textAlign = 'center';
$r.actual_size = $r.r_size + $r.r_thickness;
$r.countdown_to_time = new Date($r.countdown_to).getTime();
$r.cvs.css({ width: $r.size.w+"px", height: $r.size.h+"px" });
setInterval($r.go,$r.update_interval);
$r.go();
},
ctx: null,
go: function(){
var idx=0;
$r.time = (new Date().getTime()) - $r.countdown_to_time;
for(var r_key in $r.rings) $r.unit(idx++,r_key,$r.rings[r_key]);
},
unit: function(idx,label,ring) {
var x,y, value, ring_secs = ring.s;
value = parseFloat($r.time/ring_secs);
$r.time-=Math.round(parseInt(value)) * ring_secs;
value = Math.abs(value);
x = ($r.r_size*.5 + $r.r_thickness*.5);
x +=+(idx*($r.r_size+$r.r_spacing+$r.r_thickness));
y = $r.r_size*.5;
y += $r.r_thickness*.5;
// calculate arc end angle
//console.log(value);
if (value < 1){
value = 0;
}
var degrees = 270-(value / ring.max) * 360.0;
var endAngle = degrees * (Math.PI / 180);
$r.ctx.save();
$r.ctx.translate(x,y);
$r.ctx.clearRect($r.actual_size*-0.5,$r.actual_size*-0.5,$r.actual_size,$r.actual_size);
// first circle
$r.ctx.strokeStyle = "#efefef";
$r.ctx.beginPath();
$r.ctx.arc(0,0,$r.r_size/2,1.5*Math.PI,-0.5*Math.PI, 1);
$r.ctx.lineWidth =$r.r_thickness;
$r.ctx.stroke();
// second circle
$r.ctx.strokeStyle = "#009890";
$r.ctx.beginPath();
$r.ctx.arc(0,0,$r.r_size/2,1.5*Math.PI,endAngle, 1);
$r.ctx.lineWidth =$r.r_thickness;
$r.ctx.stroke();
// label
$r.ctx.fillStyle = "#999";
$r.ctx.font = '200 14px sans-serif';
$r.ctx.fillText(label, 0, 34);
$r.ctx.font = '200 38px sans-serif';
$r.ctx.fillStyle = "#000";
$r.ctx.fillText(Math.floor(value), 0, 8);
$r.ctx.restore();
}
};
}
var $getCountsWrapper = $(".count-down");
$getCountsWrapper.each(function( index ) {
var ringer = createRinger();
ringer.init(this);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
#canvastimer{
margin:50px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment