마지막 처리로

시간, 분, 초에 대한 시계 바늘을

삼각함수를 이용하여 자바 스크립트로 구현한다.


원리는 앞서 구현한 시간을 표현하는 것과

동일한 방식으로 계산하기 때문에

다음 내용을 읽기 보다는

직접 구현해 보는 것이 실력향상에 도움이 될 것이다.


여기서 정리할 것은

모두 앞에서 정리되었기 때문에,

여기서는 간단하게 몇 가지만 정리한다.


w3schools 예제에서 현재 시각(Date)을 구한 뒤

시간, 분, 초로 나누어서

변수(hour, minute, second)에 저장했다.

코드는 시간, 분, 초의 순서로 작성했지만

코드가 초, 분, 시간 순서로 복잡해 지기 때문에

쉬운것 부터 정리한다.

clock.html

먼저, 초를 삼각함수를 이용하여 계산하는 것은

시간과 동일하다.

X = cos (((시간 * 30) - 90) / 180) * a
Y = sin (((시간 * 30) - 90) / 180) * a

시간에서 사용된 위 코드와

21라인과 22라인의 계산 공식을 비교해 보면 동일한 것을 알 수 있다.

30 과 6 이라는 값만 차이가 있다.

시간은 360도를 12로 나누어서 시간당 30도의 값을 가진 것이고

초는 360도를 60으로 나누어서 초당 6도의 값을 가진 것 뿐이다.


다음으로 분도 같은 공식으로 계산된다.

다만, 미세하지만 초가 증가한 만큼 (6/60*second)

분의 각도를 더해 줘야 한다 [15, 16라인].

초가 증가한 만큼 분의 각도를 더해만 주면 되기 때문에

90을 빼지 않는다.

특히, 분에 대한 초의 각도는

360도를 60초 동안 가는 것으로 계산하는 것이 아니고

1분이 가는 각도 6 도를 60으로 나누어서 가기 때문에

증가한 값은 아주 적다.


마지막으로 시간도 앞서 계산한 공식과

같은 원리에 의해서 계산된다 [9, 10 라인].

분침에 초의 값을 더하는 것은 의미가 적지만

시간은 실행 결과가 조금 다를 수 있다.

따라서, 시간의 각도에 분과 초의 각도를 제대로 계산해야 한다.

예로 5시 30분인데 시침이 5시에 있다면 버그가 될 것이다.

시침은 5시와 6시 사이에 있어야 한다.

시간에 대한 분의 각도 계산은 앞서와 동일한 방식으로

1시간에 증가하는 각도 30도를

60으로 나누어서 현재 분의 값을 곱해주면 된다.

= 30 / 60 * 분


실제로 시계 바늘을 그리는 drawHand 함수에도 조금 차이가 있다.

w3schools 예제에서는

각도(pos), 바늘 길이(length), 바늘 굵기(width)가 파라메타로 필요했다.


작성된 코드의 26라인을 보면

각도와 길이 대신에 x, y 좌표로 바뀌었다.

w3schools 예제에서는 어떤 각도에 얼마큼의 길이로 그려야 했고

삼각함수에서는

중앙에서 계산된 좌표로 선을 그리면 되기 때문이다.


지금까지 2가지 방식으로

같은 기능을 구현하는 방식을 정리하였다.

어떤 방식이 더 좋다고 단정할 수 없고

개인의 선택 문제로 자신에게 알맞는 방식을 찾길 바란다.


앞서의 예제는

Canvas에서 두가지 방법으로 구현해 보았다.

여기에서는 Canvas 대신에

SVG (Scalable Vector Graphics)를 이용하여 구현한다.

SVG 장점이나 문법은 알고 있다는 전제로 정리한다.


SVG로 구현하기 전에

삼각함수로 구현한 코드

다음 코드와 같이 변경 작업을 진행해야 한다.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var center = canvas.height / 2;
//ctx.translate(radius, radius);
var radius = center * 0.90
drawClock();

function drawClock() {
  drawFace(ctx, radius);
  drawNumbers(ctx, radius);
  drawTime(ctx, radius);
}

function drawFace(ctx, radius) {
  var grad;
  ctx.beginPath();
  ctx.arc(center, center, radius, 0, 2*Math.PI);
  ctx.fillStyle = 'white';
  ctx.fill();
  grad = ctx.createRadialGradient(center,center,radius*0.95, center,center,radius*1.05);
  grad.addColorStop(0, '#333');
  grad.addColorStop(0.5, 'white');
  grad.addColorStop(1, '#333');
  ctx.strokeStyle = grad;
  ctx.lineWidth = radius*0.1;
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(center, center, radius*0.1, 0, 2*Math.PI);
  ctx.fillStyle = '#333';
  ctx.fill();
}

function drawNumbers(ctx, radius) {
    ctx.font = radius*0.15 + "px arial";
    ctx.textBaseline="middle";
    ctx.textAlign="center";
 
    var pos = radius*0.85;
    for (var num = 1; num < 13; num++) {
        var x = pos * Math.cos(Math.PI* ((30 * num)-90)/ 180);
        var y = pos * Math.sin(Math.PI* ((30 * num)-90)/ 180);

        ctx.fillText(num.toString(), x+center, y+center);
    }
}

function drawTime(ctx, radius){
    var now = new Date();
    var hour = now.getHours();
    var minute = now.getMinutes();
    var second = now.getSeconds();
    var pos = radius*0.5;
   
    //hour
    x = pos* Math.cos(Math.PI* ((hour*30)- 90 + 30/60*minute + 30/60/60*second) / 180);
    y = pos* Math.sin(Math.PI* ((hour*30)- 90 + 30/60*minute + 30/60/60*second) / 180);
    drawHand(ctx, x, y, radius*0.07);
   
    //minute
    pos = radius*0.8;
    x = pos * Math.cos(Math.PI* ((minute * 6)- 90 + 6/60*second)/ 180);
    y = pos * Math.sin(Math.PI* ((minute * 6)- 90 + 6/60*second)/ 180);
    drawHand(ctx, x, y, radius*0.07);
   
    // second
    pos = radius*0.9;
    x = pos * Math.cos(Math.PI* ((second * 6)- 90)/ 180);
    y = pos * Math.sin(Math.PI* ((second * 6)- 90)/ 180);
    drawHand(ctx, x, y, radius*0.02);
}

function drawHand(ctx, x, y, width) {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    ctx.moveTo(center,center);
    ctx.lineTo(x+center, y+center);
    ctx.stroke();
}

clock1.html

삼각함수로 구현한 코드와 차이는

translate(radius, radius)로 기준점을 화면(canvas) 중앙으로 하지 않고

원래 기준점(화면의 좌측 상단)을 유지하는 것이다.

이전 코드에서는 화면 중앙을 기준으로 했기 때문에

대부분의 좌표가 0 이었다.

좌측 상단이 0 이 되면,

도형을 그릴 위치를 지정해야 한다.


예로, 시계 외곽선을 그리기 위해

arc를 호출한다.

화면 중앙을 기준으로 할 경우

0,0 에서 반지름 만큼 그리도록 호출하면 된다 (arc(0,0,radious)).

좌측 상단을 기준으로 하면

화면 중앙에서 반지름 만큼 그리도록 호출하면 된다 (arc(center,center,radious)).

따라서 위 코드에서 보는 것 처럼 center 변수를 추가하였다.


이렇게 작성하는 것이 

책이나 인터넷 자료에서 가장 흔하게 보는 일반적인 코드 작성법이고

SVG를 좀더 쉽게 사용할 수 있어서 코드를 수정하였다.



SVG로 구현하기 전에

간단한 사용법을 익히고 진행한다.

여기에서 정리한 사용법에 맞추어

몇 가지 코드만 바꾸어 주면

Canvas를 SVG로 간단하게 구현할 수 있다.

clock2.html

위 코드 1라인에서

Canvas대신 SVG 태그를 생성하여 사용한다.

이 SVG태그를 getElementById로 찾아서

clock 변수에 담아서 자바 스크립트(JS)로 제어한다 [라인2].


Canvas에서는 도형들을 그렸지만

SVG에서는 도형이라는 HTML 태그를 생성한다.

따라서

시계의 틀(face)을 그리기 위해 큰 원(Circle)을

하나 생성하였다 [라인 15].

JS에서는 동적으로 태그를 생성할 때 createElement를 사용하지만

SVG는 도형을 동적으로 생성할 때 createElementNS를 사용한다.

생성 후에 appendChild를 이용하여 부모를 지정해 준다.

부모를 지정하지 않으면 웹 페이지에 표시 되지 않는다 [라인 16].

마지막으로 원의 속성(setAttribute)들을 지정한다.


다음 코드는 Canvas에서 사용한 코드로

시계 테두리에 그라데이션 준 것을

SVG에서는 선(원)으로 바꾸어 작성하였다.

  ctx.beginPath();
  ctx.arc(center, center, radius, 0, 2*Math.PI);
  ctx.fillStyle = 'white';
  ctx.fill();
  grad = ctx.createRadialGradient(center,center,radius*0.95, center,center,radius*1.05);
  grad.addColorStop(0, '#333');
  grad.addColorStop(0.5, 'white');
  grad.addColorStop(1, '#333');
  ctx.strokeStyle = grad;
  ctx.lineWidth = radius*0.1;
  ctx.stroke();


Canvas에서는 함수의 파라미터나

Canvas 속성을 이용하여

그리고자 하는 도형의 다양한 속성(색, 크기 등)을 지정하였다.

SVG에서는 속성으로 지정한 차이를 볼 수 있다 [라인 17~].


다음 그림은 Canvas를 사용했을때의 DOM 탐색기의 모습이다.

시계가 작동되고 있지만

Canvas 태그 하위에 아무것도 없는 것을 볼 수 있다.


다음 그림은 SVG를 사용하여

이상의 예제를 실행했을 때의 DOM 탐색기의 모습이다.

SVG 태그 하위에 원(circle) 태그가 생성된 것을 볼 수 있다.

즉, Canvas는

Canvas에 다양한 도형을 그리는 것이기 때문에

하위에 아무 것도 없다.

SVG는 다양한 도형을 태그로 생성하여 표현하는

차이를 확인할 수 있다.


이상의 코드는 몇 가지 문제가 있다.

먼저, 하나의 도형을 구현하는데 너무 많은 코드가 작성되었다.

작성할 코드가 많다는 건 일하는 시간이 많다는 의미이자

문제(버그)가 생길 가능성이 높다는 의미도 된다.


두 번째로 SVG는 HTML 태그를 이용하여 구현하기 때문에

CSS를 사용할 수 있는데 이용하지 않았다.

Canvas는 JS만 이용하여 작업하기 때문에

디자이너가 수정하기 어렵다.

CSS를 이용할 수 있다는 것은

디자인 관련 작업을 웹 디자이너나 퍼블리셔의 영역으로

넘길 수 있다는 것을 의미한다.

개발도 쉽게 할 수 있다.


이 두 가지 문제를 해결하는 방법은

다음 코드와 같이 함수화 하고,

CSS로 구현하면 된다.

clock3.html

먼저, 기본적으로 하나의 도형은

생성하고

부모를 지정하고

속성을 부여하여 사용한다.

따라서

무엇을 생성 할지 (eleType),

누가 부모인지(parent) 알려 주면 된다.

사용될 속성과 개수는 도형에 따라 다르지만

속성 이름(cx, cy 등)과 값의 구조를 가지는

Json을 이용하면 쉽게 처리 할 수 있다.

즉, createElement란 함수를 생성하고

파라미터로 도형종류, 부모, 속성을 지정하면

다양한 도형을 한 줄로 생성하여 사용할 수 있다.


다음으로 사용된 속성들은 2가지로 나눌 수 있다.

fill, stroke-width, stroke과 같이 고정된 값을 가지는 속성과

cx, cy, r과 같이

상황에 따라 바뀌는 값을 가지는 속성으로 나눌 수 있다.

고정된 값을 가지는 속성은 CSS의 클래스(face)로 처리하고 [라인 2]

바뀌는 값은 setAttribute으로 지정해 준다 [라인 23].






+ Recent posts