Orologio analogico in HTML 5 con Canvas
Orologio analogico realizzato in HTML 5 e JavaScript utilizzando i Canvas per disegnare l'orologio.
Per inserire l'orologio in pagina, crea un tag canvas:
oppure un altro tag DIV, in questo caso verrà creato all'interno un canvas con le stesse dimensioni:
per attivarlo usa questo JavaScript passando l'id del controllo:
L'orologio può essere configurato, ma in questo caso va passato un oggetto di configurazione:
ad esempio per avere un orologio con lancetta dei secondi, avanti di un ora senza visualizzare la parte digitale:
Per funzionare richiede il file seguente:
10/05/2015: ho aggiornato la funzione di redraw sostituendo setInterval con window.requestAnimationFrame di HTML 5. Rimane il fallback a setInterval per i browser che non la supportano:
Il vantaggio della funzione requestAnimationFrame è che viene gestita automaticamente dal browser secondo la frequenza di refresh supportata.
Per usarla chiamare requestAnimFrame passando la funzione di redraw del canvas. Dalla funzione di redraw richiamare ancora requestAnimFrame(cllaback) che verrà eseguita del browser al prossimo refresh.
Per inserire l'orologio in pagina, crea un tag canvas:
HTML
<canvas id="sgart-canvas-clock" style="width:70px;height:70px;"></canvas>
HTML
<div id="sgart-canvas-clock" style="width:350px;height:350px;"></div>
JavaScript
sgart.clockHtmlCanvas('sgart-canvas-clock');
JavaScript
sgart.clockHtmlCanvas({
id:'sgart-canvas-clock', //id del controllo
offsetHours:0, //eventuale scostamento orario per rappresentare altri fusi orari
backgroundColor:"#fefefe", //colore dello sfondo
borderColor:"#000", //colore del bordo
borderOn:,true, //bordo attivo
textColor:"#888", //colore del testo
digitalClockOn:true, //visualizza l'orologio digitale
refColor:"#444", //colore degli indicatore ore/minuti
refOn:true, //indicatori visibili
refMinutesOn:true, //indicatori minuti attivi
handHoursColor:"#000", //colore lancetta ore
handMinutesColor:"#000", //colore lancetta minuti
handSecondsColor:"#BF0000", //colore lancetta secondo
handSecondsOn:true, //secondi visibili
handSecondsLine:false //secondi con pallino / lancetta
});
JavaScript
sgart.clockHtmlCanvas({id:'sgart-canvas-clock', offsetHours:1, handSecondsLine:true,digitalClockOn:false });
Per funzionare richiede il file seguente:
JavaScript: sgart.clockHtmlCanvas.js
var sgart=sgart||{};
sgart.clockHtmlCanvas=function(settings){
//Sgart.it - copyright 2015 - Orologio analogio con Canvas HTML 5 / Analog clock with Canvas HTML 5
if(typeof settings==="string") settings={id:settings};
var cfg={
id:settings.id, //id del controllo
offsetHours:merge(settings.offsetHours,0), //eventuale scostamento orario per rappresentare altri fusi orari
backgroundColor:merge(settings.backgroundColor,"#fefefe"), //colore dello sfondo
borderColor:merge(settings.borderColor,"#000"), //colore del bordo
borderOn:merge(settings.borderOn,true), //bordo attivo
textColor:merge(settings.textColor,"#888"), //colore del testo
digitalClockOn:merge(settings.digitalClockOn,true), //visualizza l'orologio digitale
refColor:merge(settings.refColor,"#444"), //colore degli indicatore ore/minuti
refOn:merge(settings.refOn,true), //indicatori visibili
refMinutesOn:merge(settings.refMinutesOn,true), //indicatori minuti attivi
handHoursColor:merge(settings.handHoursColor,"#000"), //colore lancetta ore
handMinutesColor:merge(settings.handMinutesColor,"#000"), //colore lancetta minuti
handSecondsColor:merge(settings.handSecondsColor,"#BF0000"), //colore lancetta secondo
handSecondsOn:merge(settings.handSecondsOn,true), //secondi visibili
handSecondsLine:merge(settings.handSecondsLine,false) //secondi con pallino / lancetta
};
var canvas,ctx,imageBkg,h,w,prevSeconds=-1,wh,cx,cy,r,textY,textY1,textSize;
var lenHandHours,lenHandMinutes,lenHandSeconds,lenCirceSeconds,widthCirceSeconds,lenRefHours,lenRefMinutes,widthHours,widthMinutes,widthSeconds,borderSizeSmall,borderSize;
var degree360=2*Math.PI;
var degree1=degree360/60;
var degree5=degree1*5;
var degreeStart=-Math.PI/2;
var obj = document.getElementById(cfg.id);
w=parseInt(obj.style.width);
h=parseInt(obj.style.height);
if(obj.nodeName!=="CANVAS"){
//creo il canvas, lo dimensiono uguale al div e lo aggiungo
canvas=document.createElement("canvas");
obj.appendChild(canvas);
}else{
canvas=obj;
}
//imposto le dimensioni del canvas per essere sicuro di avere le dimensioni/rapporto corrette
canvas.width=w;
canvas.height=h;
//ricavo il contesto per disegnare sul canvas
ctx = canvas.getContext("2d");
//calcolo le dimensioni del cerchio che rappresenta l'orologio
wh= w>h?h:w; //trovo la dimensione minore tra altezza e larghezza ovvero il diametro
cx=w/2; //centro x
cy=h/2; //centro y
r= wh/2; //raggio
//calcolo in automatico la dimensione del bordo
borderSize=r/35; if(borderSize<1)borderSize=1;
textSize=r/5,textY=cy+r/2,textY1=cy-r/2;
borderSizeSmall=r/15; if(borderSizeSmall<1)borderSizeSmall=1;
//dimensione tacche ore/minuti e lancette
var r1=r-borderSize;
lenRefHours=r1/7; //lunghezza tacca ora
lenRefMinutes=lenRefHours*.6; //lunghezza tacca minuti
lenHandHours=r1-lenRefHours*2; //lunghezza lancetta ore
lenHandMinutes=r1-lenRefHours; //lunghezza lancetta minuti
lenHandSeconds=r1-lenRefMinutes; //lunghezza lancetta secondi
widthHours=r1/18; if(widthHours<1) widthHours=1; //larghezza lancetta ore
widthMinutes=r1/30; if(widthMinutes<1) widthMinutes=1; //larghezza lancetta minuti
widthSeconds=r1/60; if(widthSeconds<1) widthSeconds=1; //larghezza lancetta secondi
widthCirceSeconds=lenRefHours/2*.8; if(widthCirceSeconds<1) widthCirceSeconds=1; // diametro del cerchio dei secondi
lenCirceSeconds=r1-lenRefHours-lenRefHours/2; // distanza del cerchio dei secondi
//creo il background ad avvio il timer
createBackground(canvas);
//il bordo e le tacche che non cambiano mai le disegno una sola volta su un canvas a parte
function createBackground(canvas){
//creo il canvas per il background fisso
var canvasBkg=document.createElement("canvas");
canvasBkg.width=w;
canvasBkg.height=h;
var ctxBkg = canvasBkg.getContext("2d");
//pulisco il canvas
ctxBkg.clearRect(0, 0, canvasBkg.width, canvasBkg.height);
//disegno un cerchio pieno
var r1=r;
if(cfg.borderOn){
//per evitare "artefatti" sul bordo, se ho il bordo disegno un cerchio dello stesso colore
ctxBkg.beginPath(); //inizio a disegnare
ctxBkg.arc(cx,cy,r,0,degree360); //disegno un cerchio
ctxBkg.fillStyle = cfg.borderColor;
ctxBkg.fill(); //lo riempio
r1=r-1;
}
ctxBkg.beginPath();
ctxBkg.arc(cx,cy,r1,0,degree360);
ctxBkg.fillStyle = cfg.backgroundColor;
ctxBkg.fill();
//disegno le tacche dei minuti e delle ore
if(cfg.refOn){
ctxBkg.lineWidth=widthMinutes;
ctxBkg.strokeStyle=cfg.refColor;
var minutes5=5;
var lenHours=r-lenRefHours;
var lenMinutes=r-lenRefMinutes;
for(var i=0;i<60;i++){
if(cfg.refMinutesOn || minutes5===5){
ctxBkg.save(); //salvo il contesto corrente
ctxBkg.beginPath();
ctxBkg.translate(cx,cy); //modifico l'origine del contesto
ctxBkg.rotate(degreeStart+degree1*i); //ruoto il contesto
ctxBkg.moveTo(r,0); //mi sposto senza disegnare
if(minutes5===5){
minutes5=0;
ctxBkg.lineWidth=widthHours;
ctxBkg.lineTo(lenHours,0); //disegno una linea dal punto precedente a quello indicato
}else{
ctxBkg.lineTo(lenMinutes,0);
}
ctxBkg.stroke(); //disegno effettivamente il path generato (la line ain questo caso)
ctxBkg.restore(); //ripristino il contesto (translate e rotate precedenti)
}
minutes5++;
}
}
//disegno il bordo dell'orologio
if(cfg.borderOn){
ctxBkg.beginPath();
var borderSize2=borderSize/2; if(borderSize2<1)borderSize2=1;
ctxBkg.arc(cx,cy,r-borderSize2,0,degree360);
ctxBkg.lineWidth = borderSize;
ctxBkg.strokeStyle = cfg.borderColor;
ctxBkg.stroke();
}
//testo sgart.it - NON RIMUOVERE
ctxBkg.beginPath();
ctxBkg.font = "bold "+textSize+"px Arial";
ctxBkg.fillStyle=cfg.textColor;
ctxBkg.textAlign="center";
ctxBkg.textBaseline="middle";
ctxBkg.fillText("sgart.it", cx, textY1);
//setto, in base al browser, la funzione di redraw
requestAnimFrame = (function(callback) {
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| function(callback) { window.setTimeout(callback, 1000 / 60); };
})();
//genero l'immagine del background
imageBkg = new Image(); //creo unoggetto immagine
imageBkg.src = canvasBkg.toDataURL('image/png'); //converto il contenuto del canvas in una immagine rappresentata in base64 e lo carico nell'immagine
imageBkg.onload = function(){
//aspetto che l'immagine venga caricata
//richiamo l'aggiornamento
requestAnimFrame(draw);
};
};
//disegna le lancette
function draw(){
//ricavo l'ora attuale
var dt= new Date();
var seconds=dt.getSeconds();
if(seconds===prevSeconds){ //se i secondi non sono cambiati esco
requestAnimFrame(draw);
return
}
prevSeconds=seconds;
if(cfg.offsetHours!==0){
//se ho un offset orario lo imposto
dt.setHours(dt.getHours()+cfg.offsetHours);
seconds=dt.getSeconds();
}
var minutes=dt.getMinutes();
var hours=dt.getHours();
if(hours>=12) hours=hours-12; //normalizzo sulle 12 ore
//calcoli per le lancette
var hourOffset=minutes/60*degree5; //le ore si muovono nei 60 minuti
var degreeHours=degreeStart+hours*degree5+hourOffset;
var degreeMinutes=degreeStart+minutes*degree1;
var degreeSeconds=degreeStart+seconds*degree1;
//disegno lo sfondo
ctx.drawImage(imageBkg, 0, 0);
//scrivo il time
if(cfg.digitalClockOn){
var dtString=(hours>9?"":"0")+hours+"."+(minutes>9?"":"0")+minutes+"."+(seconds>9?"":"0")+seconds;
ctx.beginPath();
ctx.font = textSize+"px Arial";
ctx.fillStyle=cfg.textColor;
ctx.textAlign="center";
ctx.textBaseline="middle";
ctx.fillText(dtString, cx, textY);
}
//disegno le lancette
//hours
ctx.save(); // saves the coordinate system
ctx.strokeStyle = cfg.handHoursColor;
ctx.beginPath();
ctx.translate(cx,cy);
ctx.rotate(degreeHours);
ctx.moveTo(0,0)
ctx.lineTo(lenHandHours,0)
ctx.lineWidth=widthHours;
ctx.stroke();
ctx.restore();
//minutes
ctx.save();
ctx.beginPath();
ctx.strokeStyle = cfg.handMinutesColor;
ctx.translate(cx,cy);
ctx.rotate(degreeMinutes);
ctx.moveTo(0,0)
ctx.lineTo(lenHandMinutes,0)
ctx.lineWidth=widthMinutes;
ctx.stroke();
ctx.restore();
//second
if(cfg.handSecondsOn){
ctx.save();
ctx.beginPath();
ctx.strokeStyle = cfg.handSecondsColor;
ctx.translate(cx,cy);
ctx.rotate(degreeSeconds);
if(cfg.handSecondsLine){
ctx.moveTo(0,0)
ctx.lineTo(lenHandSeconds,0)
ctx.lineWidth=widthSeconds;
ctx.stroke();
}else {
ctx.arc(lenCirceSeconds,0,widthCirceSeconds,0,degree360);
ctx.fillStyle = cfg.handSecondsColor;
ctx.fill();
}
ctx.restore();
}
//disegno un cerchio piccolo al centro
ctx.beginPath();
ctx.arc(cx,cy,borderSizeSmall,0,degree360);
ctx.fillStyle = cfg.borderColor;
ctx.fill();
requestAnimFrame(draw);
};
function merge(v, vDefault){
return typeof v==="undefined"?vDefault:v;
}
};
JavaScript
requestAnimFrame = (function(callback) {
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.oRequestAnimationFrame
|| function(callback) { window.setTimeout(callback, 1000 / 60); };
})();
Per usarla chiamare requestAnimFrame passando la funzione di redraw del canvas. Dalla funzione di redraw richiamare ancora requestAnimFrame(cllaback) che verrà eseguita del browser al prossimo refresh.