canvas 그리기 기능을 이용해서 translate 와 rotation을 구현하는 예제로 구성해 보았습니다.
arc 와 line 을 구성하는 것으로 거의 대부분 구성이 완료되지만,
초침의 화살표는 각도에 따라 화살표 끝이 rotation 되어야 하기 때문에 화살표 그리는 부분
과, 라인을 그리는 부분을 분리하여 구성해 보았습니다.
구성한 방법은 초, 분, 시침의 rotation 각도를 가져올 때 시작하는 0도가 시침의 3시 15분 15초가 기준이 되기 때문에 0도에서 시작하기 위해 각 단위 별로 시작 값을 옮겨주는 방식을
사용하였습니다.
시침, 분침, 초침의 각도는 초침은 단독으로 구성할 수 있으나, 분침은 초침이 흐른만큼
더 움직여야 하고, 시침은 분침 초침이 움직인 만큼 더 움직여야 하기 때문에 움직임을
그에 따라 계산해 주고 있습니다.
대략 시침을 기준으로 30도 움직임을 가질 수 있고, 분은 60분을 기준으로 30도 움직일 수
있고, 초는 60초를 기준으로 0.5도를 움직일 수 있습니다. 해당 부분을 계산에 포함 시켰습니다.
아래는 구성하기 위한 함수 들 입니다.
function makeClocks(ctx, cw, ch, radious) {
let cx = cw/2;
let cy = ch/2;
ctx.clearRect(0,0,cw,ch);
var innerLineWidth = 12;
var outerLineWidth = 4;
var sOptions = getDefaultOptions();
sOptions.strokeStyle = "#008080";
sOptions.lineWidth = innerLineWidth;
drawClockLayers(ctx, cx, cy, radious, sOptions);
sOptions.lineWidth = outerLineWidth;
sOptions.strokeStyle = "#000080";
drawClockLayers(ctx, cx, cy, (radious+innerLineWidth/2+outerLineWidth+1), sOptions);
var markRadious = radious-innerLineWidth/2;
var txtRadious = markRadious-8;
drawClockTextArea(ctx, cx,cy, txtRadious, markRadious, innerLineWidth);
var now = new Date();
sOptions.strokeStyle = "#008080";
sOptions.lineCap = 'butt';
sOptions.joinCap = 'butt';
sOptions.lineWidth = 2;
drawClockSecond(ctx, cx, cy, txtRadious-4, now, sOptions);
sOptions.strokeStyle = "#000080";
sOptions.lineWidth = 4;
drawClockMinute(ctx, cx, cy, txtRadious-8, now, sOptions);
sOptions.strokeStyle = "#000080";
sOptions.lineWidth = 8;
drawClockHour(ctx, cx, cy, txtRadious-12, now, sOptions);
}
function drawClockLayers(ctx,cx,cy,radious, sOptions ) {
ctx.save();
ctx.beginPath();
setCurrentOptions(ctx, sOptions);
ctx.arc(cx,cy,radious,0, Math.PI*2);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function drawClockTextArea(ctx, cx, cy, txtRadious, markRadious, innerLineWidth ) {
ctx.save();
ctx.beginPath();
ctx.font = "12px consolas";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.lineCap = "butt";
ctx.lineWidth = 1;
ctx.beginPath();
for ( let i = 0 ; i < 12 ; i++ ) {
ctx.lineWidth = 1;
ctx.strokeStyle = "#000080";
let rd = i*Math.PI/6;
let tx = txtRadious*Math.cos(rd)+cx;
let ty = txtRadious*Math.sin(rd)+cy;
let numStr = ((3+i)%12);
if ( numStr == 0 ) {
numStr = 12;
}
ctx.strokeText(numStr, tx,ty);
ctx.lineWidth = 2;
ctx.strokeStyle = "#00FFFF";
let tsx = (markRadious)*Math.cos(rd)+cx;
let tex = (markRadious+innerLineWidth)*Math.cos(rd)+cx;
let tsy = (markRadious)*Math.sin(rd)+cy;
let tey = (markRadious+innerLineWidth)*Math.sin(rd)+cy;
ctx.moveTo(tsx,tsy);
ctx.lineTo(tex,tey);
ctx.stroke();
}
ctx.closePath();
ctx.restore();
}
function drawArrowPoints(ctx, tx, ty, radians,pw,ph,sOptions ) {
ctx.save();
setCurrentOptions(ctx, sOptions);
ctx.beginPath();
ctx.translate(tx,ty);
ctx.rotate(radians);
ctx.moveTo(ph/2,0);
ctx.lineTo(-ph/2,-pw/2);
ctx.lineTo(-ph/2,pw/2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
}
function drawClockSecond(ctx, cx, cy, radious ,now, sOptions) {
ctx.save();
setCurrentOptions(ctx, sOptions);
let s = now.getSeconds();
let rd = ((s-15)*Math.PI*6)/180;
let tx = Math.cos(rd)*radious+cx
let ty = Math.sin(rd)*radious+cy;
ctx.beginPath();
ctx.moveTo(cx,cy);
ctx.lineTo(tx,ty);
ctx.stroke();
ctx.closePath();
if ( !sOptions ) {
sOptions = getDefaultOptions();
}
sOptions.strokeStyle = "blue";
sOptions.fillStyle = "#008080";
sOptions.lineWidth = 1;
sOptions.lineCap = 'butt';
sOptions.joinCap = 'butt';
drawArrowPoints(ctx, tx,ty, rd, 6,12, sOptions);
ctx.restore();
}
function drawClockMinute(ctx, cx, cy, radious , now, sOptions) {
ctx.save();
setCurrentOptions(ctx, sOptions);
let s = now.getSeconds();
let m = now.getMinutes();
let rd = ((m-15)*6+s/10)*Math.PI/180;
let tx = Math.cos(rd)*radious+cx
let ty = Math.sin(rd)*radious+cy;
ctx.beginPath();
ctx.moveTo(cx,cy);
ctx.lineTo(tx,ty);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function drawClockHour(ctx, cx, cy, radious , now, sOptions) {
ctx.save();
setCurrentOptions(ctx, sOptions);
let s = now.getSeconds();
let m = now.getMinutes();
let h = now.getHours();
let rd = (((h-3))*30+m/2+s/120)*Math.PI/180;
let tx = Math.cos(rd)*radious+cx
let ty = Math.sin(rd)*radious+cy;
ctx.beginPath();
ctx.moveTo(cx,cy);
ctx.lineTo(tx,ty);
ctx.stroke();
ctx.closePath();
ctx.lineWidth = 2;
ctx.fillStyle = "#00FFFF";
ctx.strokeStyle = "#0000FF";
ctx.beginPath();
ctx.arc(cx,cy,6,0,Math.PI*2);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.arc(cx,cy,6,0,Math.PI*2);
ctx.stroke();
ctx.closePath();
ctx.restore();
}
아래는 해당 소스의 실행 결과 입니다.