메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

디지털라이프

크로스-브라우저 애니메이션

한빛미디어

|

2002-08-26

|

by HANBIT

12,513

저자: 작성데이브 쏘우(Dave Thau), ADC(Apple Developer Connection), 역 전순재

애니메이션 이면의 기본적인 아이디어는 아주 오랜 기간동안 답보상태로 남아있었다. 일단의 이미지들이 빠르게 표시되면 연속적으로 보여지는 이미지들이 마치 하나의 움직이는 그림을 형성하는 것처럼 보인다는 개념으로부터 많은 발전은 없었다. 이러한 효과를 보여주는 첫 번째 예가 바로 회전그림판(thaumatrope)으로 이는 1828년에 폴 로제(Paul Roget)가 고안한 간단한 장치(gizmo)에 불과했다. 회전그림판은 막대기나 줄이 붙어 있어서 빙빙 돌 수있는 디스크형태로 되어있다. 디스크의 각 면에는 삽화가 있는데 디스크를 빙빙 돌리면 두 삽화가 합쳐져서 마치 한 개의 이미지만 있는 것처럼 보인다. 디스크 한 면에 새가 있고 다른 한 면에 새장이 있는 경우, 디스크를 돌리면 새장 속에 새가 갖혀있는 듯한 단 하나의 이미지만 남게 된다.

웹 상에서의 애니메이션도 비슷한 방식으로 작동한다. 초기 웹의 시대에 애니메이터들은 쇼크웨이브(Shockwave)나 애니메이션 GIF와 같은 독점적인 시스템만 사용할 수밖에 없었다. 자바스크립트는 신속한 GIF 교환을 애니메이터의 작업 목록에 포함시켜 더욱 확장되었다. 자바스크립트로 GIF를 교환하면 독점 시스템보다 더 빨리 내려 받을 수 있게 되고 애니메이션 GIF보다 더 큰 유연성을 더 제공한다.

동적 HTML(DHTML)은 페이지를 애니메이션하기 위한 새로운 방법들을 광범위하게 제공한다. DHTML은 텍스트와 이미지 모두를 애니메이션화 할 수 있으며, 애내메이션은 한 점에 고정되어 있는 것이 아니라 브라우저 창을 이리저리 돌아다닐 수 있다. 안타깝게도 DHTML은 브라우저들 사이의 차이점 때문에 더 교묘할 수도 있다.

이 기사는 크로스-브라우저 애니메이션의 기본에 대해 다룰 것이다. 텍스트와 이미지를 애니메이션하는 법은 물론이고 화면에 HTML 요소들을 이리저리 옮기는 법을 배울 수 있을 것이다. 이 기사를 다 읽고 나면 틀림없이 여러분은 크로스-브라우저 호환되는 DHTML 애니메이션을 웹 페이지에 추가할 수 있으리라 생각된다.

이미지 크기조절하기

많은 웹 페이지들은 마우스오버 이벤트를 이용해 크기가 변하는 이미지를 만든다. 이런 테크닉을 사용하면 버튼이 튀어 오르게 하거나 작은 이미지를 눈에 더 보이기 쉽게 만들 수 있다. 거대한 크기의 파일인 애니메이션 gif를 사용하는 대신에, 또는 미리 적재된 이미지를 뜻하는 이미지 교환을 사용하는 대신에, DHTML로 이미지의 크기를 변경할 수 있다.

IE4.0+와 NN 6.0+에서 이미지의 크기를 변경은 상당히 간단하게 작업할 수 있다. 이미지의 height와 width의 특성만 변경하면 되기 때문이다.
document.my_image.height = "100px";
document.my_image.width = "200px";
그렇지만 넷스케이프 4.0에서는 자바스크립트를 사용하여 height와 width의 특성을 변경할 수 없다. 여기에서 최선의 방법은 이미지를 DIV 안에 집어 넣고 자바스크립트를 사용하여 그 DIV를 다시 쓰는 것이다.
document.the_div.document.write("");
document.the_div.document.close();
첫 번째 라인은 DIV의 내용을 화면에 쓴다. 두 번째 라인은 문서를 닫는데, 이렇게 해야 종종-과묵한 네트스케이프(Netscape)에게 강제로 여러분이 변경한 것을 웹 페이지에 쓰도록 만들 수 있다.
Designing with JavaScript, 2nd Edition


꾸준한 성장

위의 라인들을 보면 이미지 크기를 변경하는 법을 볼 수 있지만 이것이 실제 애니메이션은 아니다. 단 한번에 왕창 변경하는 대신 이미지의 크기가 천천히 커지기를 원한다면 더욱 애니메이션적인 테크닉을 사용하면 된다.

이미지를 아주 조금만 변경하려면 잠시 동안 기다린 다음, 이미지를 조금 더 변경해라. 자바스크립트로 이렇게 하려면 이미지를 아주 조금씩 변경하는 함수를 작성해야 한다. 그리고 나서 setTimeout()이라고 부르는 명령어를 사용하여 잠시 후에 다시 그 함수를 호출해라. 앞으로 그 함수가 다시 호출되면 이미지는 다시 변경되고, setTimeout()은 그 함수가 다시 잠시 후에 호출되도록 설정한다. 이런 시간제한 루프는 이미지에 활력을 불어넣는다. 함수 호출 사이의 시간의 양은 애니메이션 속도를 결정한다.

setTimeout() 함수는 다음과 같이 작동한다.
var my_timeout = setTimeout("someFunction();", 
some_duration_in_milliseconds);
setTimeout() 함수의 첫 번째 매개변수는 앞으로 언젠가 호출하고자 하는 함수의 이름을 결정한다. 두 번째 매개변수는 기다리고자 하는 시간을 1000분의 1초 단위로 설정한다(1초는 1000 밀리초). 그래서 앞으로 2초 후에 함수를 호출하고자 한다면 두 번째 매개변수는 2000이 되어야 한다.

위의 함수에서 my_timeout이라고 불리는 변수를 설정한 것에 주목하자. 자바스크립트는 앞으로 모든 setTimeout() 함수를 이 변수를 통하여 참조할 것이다. 이런 시간 제한을 멈추고자 한다면, clearTimeout() 함수를 사용하고 my_timeout 변수를 참조하면 된다.
clearTimeout(the_timeout);
다음은 이미지 크기를 변경하고 setTimeout()을 사용하여 자신을 호출하는 함수의 예이다.
function growImage()
{
  document.my_image.width = parseInt(document.my_image.width) + 10;
  document.my_image.height = parseInt(document.my_image.height) + 10;

  if (parseInt(document.my_image.width) < 200) 
  {
    my_timeout = setTimeout("growImage();", 100);
  }
}
위 함수에서 첫 번째 두 라인은 IE4+와 NN6+에서 이미지 크기를 변경한다. if-then 서술문은 시간제한을 설정하여 이미지의 너비가 199 픽셀을 넘지 않을 때까지 10분의 1초 단위로 growImage() 함수를 호출한다. 이미지의 크기를 획득하고자 parseInt() 함수를 사용한 것에 주목하자. 이미지의 너비를 읽을 때 어떤 브라우저들은 "50px"와 같은 것을 반환하곤 한다. 이것은 숫자 10에 더할 때 문제를 발생시킬 수 있다. parseInt() 함수는 문자열에서 발견하는 첫 번째 정수를 나포하기 때문에 이미지의 widthheight 특성이 수치 값으로 설정될 것이라고 확신해도 좋다.

커지는 버튼 데모와 그 소스 코드를 살펴 보면 이미지 크기 변경하는 크로스-브라우저 버전을 볼 수 있을 것이다.

브라우저에 따라 성장

커지는 버튼 데모에는 약간의 크로스-브라우저 호환 코드가 있는데 이는 충분히 연구할 만한 가치가 있다. 이미지를 DIV에 집어 넣고 있는 것에 주목하자.

이렇게 한 까닭은 넷스케이프 4.0이 동적인 이미지 크기 변경을 허용하지 않기 때문이다. 넷스케이프 4.0에서는 이런 DIV의 내용을 다시 화면에 뿌려서 이미지를 변경해야 한다. 다른 브라우저에서는 작업하기가 더 쉽다. 그냥 이미지의 height와 width 특성을 변경하기만 하면 되기 때문이다. 다음은 이미지를 마우스오버 시에 커지도록 만드는 함수이다.
function makeBig()
{
  var the_image;

  // 만약 브라우저가 NN6나 IE4+라면
  if (document.documentElement || document.all) 
  {
    the_image = document.the_button;
    the_image.height = the_image.height + 2;
    the_image.width = the_image.width + 2;
  } 
  else if (document.layers)  // NN4
  {

    the_image = document.justforNS4.document.the_button;
    var new_width = the_image.width + 2;
    var new_height = the_image.height + 2;
   
    var write_string string = "";    

    document.justforNS4.document.writeln(write_string);
    document.justforNS4.document.close();
  }

  if (parseInt(the_image.height < 110)
  {
    setTimeout("makeBig();", 10);
  }
}
일단 이 함수는 어떤 이미지 변경 방법을 사용해야 하는가를 점검한다. W3C DOM 준수 브라우저와 IE4+라면 이미지 크기를 변경하고, 그렇지 않고 넷스케이프 4라면 DIV를 재작성하는 것이 바로 점검사항이다. W3C DOM을 준수하는 브라우저(Netscape 6.0+, IE 5+)는 document.documentElement 특성을 인식하고, 모든 IE4+ 브라우저들은 document.all 특성을 인식하기 때문에, 다음 라인은
if (document.documentElement || document.all)
이미지를 변경할 수 있도록 해주는 모든 브라우저들을 알아챌 것이다. 만약 브라우저가 document.documentElementdocument.all을 인식하지 않으면, 스크립트는 그 브라우저가 넷스케이프 4의 특성인 document.layers를 인식하는지 점검한다. 그런 후, 이 코드는 DIV의 내용을 화면에 다시 뿌린다.

이미지 크기가 변경되고 나면 함수는 그 이미지가 너무 크게 되지는 않는지 점검한다. 이미지의 height가 110 픽셀보다 작다면 이 함수는 setTimeout()을 사용하여 자신을 10 밀리초 단위로 호출한다.

텍스트 애니메이션

애니메이션 텍스트는 애니메이션 이미지와 매우 유사하다. 그러나 여기에서는 이미지의 heightwidth를 변경하는 대신, 텍스트의 fontsize를 변경할 것이다. 이는 IE5+이나 NN6+에서 다음과 같이 할 수 있다.
var my_div = document.getElementById("myDiv");
my_div.style.fontSize = "36px";
IE5/Mac을 사용한다면 주의해야 한다. 이 브라우저에서는 만약 위치가 지정된(positioned) 요소 안에 그 폰트가 있다면 폰트 스타일 정보를 변경할 수 없기 때문이다. IE5/Mac에서 텍스트에 활력을 불어 넣고자 한다면 그 텍스트가 위치가 지정된 요소 안에 있지 않다는 것을 확실하게 해두어야 할 필요가 있다.

다음 코드는 IE4에서 폰트를 변경한다.
var my_div = document.all.myDiv;
my_div.style.fontSize = "36px";
그리고 NN4에서는 다음과 같다.
var my_div = window.myDiv;
document.my_div.document.write("This is my text);
document.my_div.document.close();
춤추는 문자 데모는 이러한 종류의 작업이 실제로 실행되는 것을 보여준다(IE5+와 NN6+ 전용). 각 문자는 자신만의 SPAN에 있다.
h
애니메이션은 각 SPAN을 통과하면서 fontSize를 자신의 스타일에 맞추어 변경한다.
var timeout;
function dance()
{    

  var mySpans = document.getElementsByTagName("span");
  var size;
  var loop = 0;

  while (mySpans.item(loop))
  {
    size = Math.floor(Math.random() * 60) + 12;
    mySpans.item(loop).style.fontSize = size;
    loop++;
  }

  timeout = setTimeout("dance();",500);
}
함수에서 앞의 세 줄은 몇 개의 변수를 선언하고 있다. 첫째 값은 페이지에 있는 모든 SPAN들을 담은 목록을 획득한다. 일단 그 변수들이 설정되면 while 루프는 item() 메소드를 사용하여 SPAN 리스트를 통과한다. 통과할 때마다 random() 메소드는 무작위 수를 12에서 72까지 발생시키고, 적절한 SPAN의 fontSize는 그 숫자로 변경된다. getElementsByTagName()이나 item() 메소드에 익숙하지 않다면 "the new DOM"이라는 기사를 한번 읽어보기 바란다.


자바스크립트 핵심 가이드

참고 도서

자바스크립트 핵심 가이드
데이비드 플라나긴




비록 이런 종류의 작업이 IE5+와 NN6+에서는 상대적으로 쉽기는 하지만, NN4에서는 상당히 교묘하다. NN4에서는 이미지의 크기를 변경할 때 그랬던 것처럼 그 단어를 DIV 안에 집어 넣고 그 DIV의 내용을 다시 화면에 뿌려야 한다. 다음은 생동감 넘치는 텍스트 크기 변경을 보여주는 크로스-브라우저 버전이다

문자들이 약간 더 부드럽게 움직이기를 원한다면 다음의 부드럽게 커지는 문자 데모를 한번 살펴 보기 바란다. 소스를 보면 어떻게 작동하는지 알 수 있다.

그림 움직이기

지금까지 그림과 텍스트를 적절하게 애니메이션하는 법을 살펴 보았다. 이 섹션에서는 화면 주위로 사물들을 움직이는 법을 보여 주겠다.

DIV는 동적으로 자신의 lefttop 특성을 변경해주면 움직일 수 있다. 예를 들어 "myDiv"라는 신분번호(id)를 가진 DIV를 움직이려면 hiding and showing layers라는 기사에서 소개한 getStyleObject() 함수를 사용하여 DIV의 스타일 시트를 획득한 후 자신의 lefttop 특성을 변경하면 된다. 약간 복잡한 점이 하나 있다면 넷스케이프 4는 이러한 값들을 숫자로 인식한다는 것이다. 만약 이러한 특성들에 문자열을 넣으면 에러가 날것이다. 다른 브라우저들은 숫자들이 문자열이라고 예상한다. 그래서 그 숫자가 픽셀(pixels) 단위라면 숫자 다음에 "px"를 붙여 포맷한다.

다음 예제는 DIV를 이곳 저곳으로 이동시킨다. 예제의 소스를 살펴 보면 링크에 클릭을 걸어주면 jump() 함수를 촉발시키는 것을 보게 될 것이다. 이 함수는 DIV의 스타일시트를 획득하고 그것을 약간 오른쪽 아래로 옮긴다. DIV를 움직여주는 핵심 라인은 다음과 같다.
var the_style = getStyleObject("myDiv");

var the_left = parseInt(the_style.left) + 100;
var the_top = parseInt(the_style.top) + 100;
if (document.layers)
{
  the_style.left = the_left;
  the_style.top = the_top;
}
else 
{
  the_style.left = the_left + "px";
  the_style.top = the_top + "px";
}
일단 스크립트는 getStyleObject() 함수를 사용하여 스타일 시트를 획득한다. 그 다음에 DIV의 topleft 좌표에 대해 새로운 값을 계산한다. 다음으로 계산한 새로운 값을 그 DIV의 lefttop 특성에 할당한다. 페이지를 보고 있는 브라우저(넷스케이프 4)가 document.layers를 지원하면 스크립트는 새로운 숫자를 lefttop 특성에 할당한다. 브라우저가 넷스케이프 4가 아니라면 스크립트는 "px"를 숫자끝에 붙이고 그것을 lefttop 특성에 할당한다.

간단한 경로를 따라 움직이기

DIV를 급격히 움직이는 대신에 부드러운 동선으로 움직이도록 하려면 이미지와 문자의 크기 변경하기 섹션에서 보여준 것과 똑같은 setTimeout() 트릭을 사용해야 한다. 부드럽게 DIV를 화면을 가로질러 옮기는 데모를 한번 살펴보자. 앞으로도 보겠지만 이 예제의 소스를 한번 살펴보면 moveDiv() 함수가 DIV를 아주 조금만 움직일 뿐이고 setTimeout()를 사용하여 잠시 후에 자신을 호출하는 것을 볼 수 있다. 이것이 애니메이션의 핵심이다. 시간에 따른 수많은 작은 차이가 부드러운 움직임을 모사하는 것이다.

moveDiv() 함수에는 약간의 트릭이 있는데 이 트릭은 DIV가 일정한 점에 도달하면 그 DIV를 정지시킨다는 것이다. setTimeout() 호출을 if 블록 안에 배치하면 이미 멀리 가버렸을 때 그 DIV의 움직임을 멈출 수 있다.
    if (new_left < 400)
    {
      the_timeout = setTimeout("moveDiv();",10);
    }
그렇지않고 그 DIV가 일정 지점에 도달하면 방향을 틀도록 만들 수도 있으며 또는 시작했던 지점에서 다시 시작하도록 만들 수도 있다.

더 복잡한 경로를 따라 움직이기

위의 해결책은 DIV가 상대적으로 간단한 자취를 따라 움직이고자 할 때 동작한다. smooth_move.html에서 DIV는 그냥 직선을 따라 움직인다. 그러나 DIV가 더욱 복잡한 자취, 예를 들어 원과 같은 더욱 불규칙한 자취를 따라 움직이기를 원한다면 코드가 더 필요할 것이다. 수학을 좋아한다면 수학 공식을 사용하여 DIV가 각 단계마다 어디로 갈지를 결정할 수 있다. 라인에 대한 공식(y=mx+b)은 상대적으로 자바스크립트 안으로 코드 해넣기가 쉽다. 원에 대한 공식(y^2 + x^2 = 1)은 더 어렵다. 그리고 자취가 이미 널리 알려진 기하학적 객체를 따르지 않는다면 처음부터 공식을 가지고 대응하는 것은 힘들 가능성이 많다.

수학 교과서의 악몽을 떠올리고 싶지 않다면 그냥 경로를 기술하는 지점들을 담은 배열을 만들어 DIV를 이점 저점으로 움직이면 된다. 배열에 지점들이 많을수록 경로는 더 길고 더 부드럽게 될 것이다. 사각춤 예제는 DIV를 사각형 안에서 움직이게 만드는 방법을 보여준다. 소스 코드를 들여다보면 지점들을 담고 있는 배열이 있는 것을 볼 수 있을 것이다. 모든 점들을 방문하고 난 후, 코드는 배열의 처음에서부터 다시 시작한다. 배열에서 각 요소들은 예를 들어 "160:100"과 같이 콜론으로 분리된 숫자 두 개 가지는 문자열이다. 이 숫자들은 lefttop 위치를 나타낸다. 나는 split 함수를 사용하여 각 배열 요소를 두 개의 위치로 분리하였다.
   var the_points = next_point.split(":");

   var left = the_points[0];
   var top = the_points[1];
split() 함수는 문자열 하나를 취해 제공한 구분자(delimiter)에 근거하여 그것을 배열로 조각낸다. split()가 여러분 대신 일을 처리해주므로 미리 배열을 만들 필요가 없다. 따라서 위의 라인에서 문자열 "160:100"은 두 개로 조각나서 the_points 안으로 적재된다. the_points에서 첫번째 요소인 the_points[0] 은 160이 될 것이다. 그리고 두 번째 요소인 the_points[1] 은 100이 될 것이다.

다음 함수는 DIV를 이동시킨다.
function moveDiv(array_position)
{
  // 스타일시트를 획득한다
  //
  var the_style = getStyleObject("myDiv");
  if (the_style)
  {
    // 배열에서 다음 지점으로 간다
    //
    array_position = array_position + 1;
    
    // 배열의 끝을 지났다면, 
    // 위치 0에서 시작한다
    //
    if (array_position >= the_coords.length) {
      array_position = 0;
    }
    
    // 지점 "120:100"을 요소를 두 개 가진 배열로 변형한다
    // : left와 top요소를 가진다.
    //
    var next_point = the_coords[array_position];
    var the_points = next_point.split(":");
    var left = the_points[0];
    var top = the_points[1];

    // 이제 left와 top의 특성을
    // 적절하게 설정한다
    //
    if (!document.layers) 
    {
      left = left + "px";
      top = top + "px";
    }
    the_style.left = left;
    the_style.top = top;

    // 그리고 현재 배열 위치를 가지고
    // 다시 moveDiv()를 호출한다
    the_timeout = setTimeout("moveDiv(" + array_position + ")",100);
  }
}
moveDiv() 함수에 대한 호출에는 애니메이션이 자취를 따라 다음으로 가야 할 곳을 표현하는 숫자 하나가 동반된다. 다시 말해 그 숫자는 어느 요소가 배열에서 다음으로 처리될 필요가 있는지를 가리킨다. 애니메이션을 촉발하는 링크는 배열의 첫 번째 요소인 0번 요소에서부터 시작한다.
start moving! 
위 함수는 먼저 DIV에 대한 스타일시트 정보를 획득한다. 그런 후, array_position에 0을 더해 배열에서 다음 좌표를 알 수 있도록 한다. 새로운 array_position이 자취 배열의 끝을 넘어서면 array_position은 0으로 재설정된다.

일단 array_position을 확보하면 배열로부터 좌표를 얻는데, split() 함수를 사용하여 각각의 특성을 따로 결정하고나서 DIV에 topleft의 특성을 설정한다.

마지막으로, 이 함수는 setTimeout()를 사용하여 자신을 다시 호출한다. 마지막 라인이 약간 이상하게 보이는 점에 주목하자.
the_timeout = setTimeout("moveDiv(" + array_position + ")",100);
다음 코드가 작동할 거라고 생각할지도 모르겠다.
the_timeout = setTimeout("moveDiv(array_position)",100);
안타깝게도 array_position 변수는 moveDiv() 함수 안에서만 존재한다. moveDiv() 함수가 종료하면 array_position 변수는 사라진다. 100 밀리초 단위로 setTimeout() 함수가 moveDiv() 함수를 다시 호출하려고 시도하면 자바스크립트는 array_position의 값을 찾아 보려고 시도할 것이다. moveDiv() 함수의 반복이 끝날 시점에 자바스크립트는 그 변수가 무엇인지 알지 못하므로 에러가 발생한다.

이와 같은 성가신 문제를 극복하기 위해 변수들을 setTimeout()의 함수 호출에 넘겨주지 않고 대신에 그 변수들의 값들을 넘겨주는 것이다.

복잡한 경로를 부드럽게 만들기

위에 보여준 배열처리 방법을 사용하면 움직임이 거칠게 될 가능성이 있다. 배열에서 지점의 갯수는 그 경로의 거친 정도를 결정할 것이다. 점이 많을수록 움직임은 더 부드러워진다. 그러나 많은 점들을 배열에 배치하면 각 점을 계산하는데 엄청난 시간이 걸릴 수도 있기 때문에 문제가 될 수 있다. 더 빠르고 더 우아한 해결책은 배열에 있는 점들을 앵커(anchors)로 사용하는 것이다. 그리고 setTimeout() 함수를 사용하여 앵커 점들 사이를 부드럽게 움직이도록 만드는 것이다. 이렇게 해준다고 해서 항상 정확하게 원하는 경로를 따라 움직일 수는 없지만 충분히 훌륭한 테크닉으로 종종 이용할 수는 있다. 이 테크닉은 분주한 벌 예제에서 볼 수 있다.

이 예제의 소스 코드를 살펴 보면 소스에 다른 코드보다 더 많은 코드를 포함하고 있다는 것을 알 수 있을 것이다. 여기에 사용된 주요 함수는 getAnchors()moveDiv()같이 두 개가 있다.

getAnchors() 함수는 자취에서 각 구역의 시작점과 끝점을 찾아서 이 숫자들을 moveDiv() 함수에 되먹인다. moveDiv() 함수는 이전에 논의한 setTimeout() 트릭을 사용하여 이미지를 옮긴다. moveDiv() 또한 이미지가 끝 앵커 점을 지나 움직이지는 않는지를 점검하기도 한다. 이미지가 구역의 끝점을 지나 움직이기 시작하면 moveDiv()는 이미지를 끝 앵커 점으로 옮기고 난 후 getAnchors()를 호출하여 다음 앵커 쌍을 획득한다.

getAnchors() 함수는 간단하다. 이 함수는 자취의 현재 구역을 매개변수로 취해서 적절한 시작점과 끝점(anchors)을 열람한다. array_position이 0 이라면 getAnchors() 함수는 첫 번째 앵커(the_coords[0])와 두 번째 앵커 (the_coords[1])를 찾는다.
var first_anchor = the_coords[array_position];
var second_anchor = the_coords[array_position+1];
그리고 나서 1을 array_position 변수에 더하고 배열의 끝에 도달하지 않았음을 확인한다. 배열의 끝이라면 getAnchors() 함수는 위치를 0으로 재설정하는데, 이는 애니메이션이 처음부터 다시 시작할 것이라는 것을 뜻한다.
array_position++;
if (array_position == the_coords.length-1)
{
  array_position = 0;
}
다음 구역을 결정한 후, 이 함수는 moveDiv()를 호출하여 실제로 DIV를 이동시킨다.
moveDiv(array_position, first_anchor, second_anchor, 0, 0);
moveDiv()에 보내지는 전반부의 변수 세 개는 각각 현재 배열 요소, 첫 번째 앵커의 좌표, 두 번째 앵커의 좌표를 가리킨다. 후반부의 매개변수 두 개는 얼마나 멀리 DIV를 수직과 수평으로 이동시키는지를 추적 유지한다. 처음에는 그 값들이 무엇인지 알 수 없을 것이다. 그래서 여기에서는 그냥 0을 넣어 두기만 하면 된다. 매번 DIV를 얼마나 이동시킬지는 moveDiv() 함수에 달려 있다.

moveDiv() 함수는 getAnchors() 함수보다 더 복잡하다. 먼저 getStyleObject() 함수를 사용하여 스타일시트 정보를 획득한다. 다음 라인은 각 앵커의 left와 top 좌표를 획득한다. 다음 코드는 첫 번째 앵커를 위한 것이다.
    var first_points = anchor_one.split(":");
    var first_left = parseInt(first_points[0]);
    var first_top = parseInt(first_points[1]);
여기에 다시 split() 함수가 사용되고 있는데 "100:120"처럼 보이는 앵커를 두 개의 숫자로 분리한다.

그리고 나서, moveDiv() 함수는 계산할 필요가 있다면 수직과 수평으로 건너 뜀 크기를 계산한다. lefttop 건너뜀 크기가 0으로 설정된 채로 getAnchors()에 의해서 moveDiv()가 호출될 때 각 세그먼트의 시작에서만 계산하면 된다. getStepSize() 함수는 숫자 하나를 반환하는데 이 숫자는 moveDiv()가 호출될 때마다 DIV가 그 방향으로 얼마나 많이 움직여야 하는지를 결정한다. getStepSize()는 매개변수를 세 개 취한다. 두 앵커의 좌표와 수평 또는 수직 건너뜀 크기를 비롯하여 어느 것이 알고 싶은지를 나타내는 숫자 하나가 그것이다.(수평이라면 0이고 수직이라면 1)
    if ((horizontal_step_size == 0) && (vertical_step_size == 0))
    {
      horizontal_step_size = 
        getStepSize(anchor_one, anchor_two, 0);

      vertical_step_size = 
        getStepSize(anchor_one, anchor_two, 1);
    }
getStepSize()에 대해서는 조만간 더 자세하게 설명하겠다. 건너뜀 크기가 결정되면 DIV의 현재 위치에 그 건너뜀 크기를 더해 새로이 lefttop 좌표가 계산된다.
    var new_left = first_left + horizontal_step_size;
    var new_top = first_top + vertical_step_size;
DIV를 움직이기 전에 구역의 끝 앵커를 지나치지 않을 것이라는 것을 확실히 해둘 필요가 있다. 이것은 실제로 처음에 짐작했던 것보다 더 트릭이 필요하다. 나는 atEndOfPath()라고 부르는 함수를 따로 작성하여 DIV가 끝 앵커를 지나치기 시작하는지 알아 보았다. atEndOfPath() 함수는 건너 뜀 크기와, 구역의 마지막 위치, moveDiv() 함수가 DIV를 어디로 옮기기를 원하는지에 대해 알아두어야 한다. 이러한 숫자에 근거하여 atEndOfPath()는 DIV가 구역의 마지막을 지나치기 시작하면 참(true)을 반환하고 그렇지 않으면 거짓(false)을 반환한다. 구역의 마지막을 지나서 움직이기 시작하면 대신에 DIV는 마지막 앵커로 움직여야 한다.
    if (atEndOfPath(left_step_size, second_left, new_left) 
       ||(atEndOfPath(top_step_size, second_top, new_top)))
    {
      new_left = second_left;
      new_top = second_top;
    }
이제 넷스케이프 네비게이터 4가 페이지를 렌더링 하지 않는 한 DIV를 움직이기 전에 해야 할 일은 "px"를 좌표의 끝에 추가하는 작업뿐이다.
    if (!document.layers) 
    {
      new_left = new_left + "px";
      new_top = new_top + "px";
    }
그리고 결국, DIV를 움직일 수 있다.
    the_style.left = new_left;
    the_style.top = new_top;
그러나 DIV가 움직인 후에도 해야 할 일이 더 있다. 스크립트가 다음으로 무엇을 해야 할지 알아야 한다. 이에는 다음과 같은 두 가지 상황이 있다. 스크립트가 구역의 끝에 도달하면 getAnchor()를 호출하여 다음 구역에 대하여 작업을 시작해야만 한다. 만약 스크립트가 구역의 끝에 도달하지 않았다면 moveDiv()를 잠시 후에 다시 호출하여 DIV를 그 구역을 따라 약간씩 이동시켜야 한다.

이 작업의 전반부는 별로 어렵지 않다.
    if ((parseInt(new_left) == parseInt(second_left)) && 
            (parseInt(new_top) == parseInt(second_top)))
    {
      getAnchors(array_position);
    }
DIV가 구역의 끝이라면 getAnchors()를 호출하여 다음 앵커 집합을 획득해야 한다. 두 번째 부분은 약간의 트릭이 더 필요하다. 정확한 매개변수를 가지고 moveDiv()를 다시 호출해야 한다. moveDiv()에 대한 호출에는 다음과 같은 다섯 개의 매개변수가 있다. 배열의 자취가 얼마나 긴가, 움직임 시작될 위치, 끝나는 곳의 위치, 수평 건너뜀 크기, 수직 건너뜀 크기. 아마도 다음과 같을 것이다.
moveDiv(1, "120:100", "140:100", 10, 0);
함수에서 후반의 세 줄은 문자열을 만들고난 후, setTimeout()를 사용하여 함수를 100분의 1초마다 호출한다.
   var new_anchor_one = new_left + ":" + new_top;

     var timeout_string = "moveDiv(" +
        array_position + ", "" + new_anchor_one + "", "" +
        anchor_two + "", " + left_step_size + "," + 
        top_step_size + ");";

      the_timeout = setTimeout(timeout_string, 10);
다음 함수는 DIV가 구역 경계를 넘어가기 시작했는지 아닌지를 결정한다.
function atEndOfPath(the_step_size, second_number, new_number)
{
    var the_end = false;

    if (((the_step_size > 0) && (new_number > second_number)) ||
        ((the_step_size < 0) && (new_number < second_number)))
   {
     the_end = true;
   }

   return the_end;
}
이 함수가 취하는 매개변수는 각각 매번 DIV가 이동해야 할 양, 세그먼트의 끝, DIV가 움직이기 시작할 위치를 가리킨다. 고려해야 할 조건은 다음과 같이 두 가지가 있다. 만약 the_step_size가 양수라면 이것은 DIV가 오른쪽이라 아래로 움직인다는 것을 뜻한다. 따라서 new_number 매개변수(다음에 움직일 건너 뜀크기)가 second_number보다 크다면 DIV는 너무 멀리 가버릴 것이다. 반면, the_step_size가 음수라면 이것은 DIV가 왼쪽이나 위로 움직일 것이라는 것을 뜻한다. 따라서 new_numbersecond_number보다 작다면 DIV는 너무 멀리 가버린다.

마지막 함수는 건너뜀 크기를 알아내는 것으로 아주 간단하다. 그저 첫 번째 점과 두 번째 점의 적절한 좌표를 획득하고 그 좌표 사이의 거리를 십 등분 하면 된다. 이것은 곧 각 세그먼트가 10개의 조각으로 쪼개질 것이라는 것을 의미하며, moveDiv()는 각 세그먼트당 10 번 호출될 것이라는 것을 뜻한다.

이게 다이다. 보다시피, 우리가 이전에 본 다른 어떤 예제들보다 상당히 더 복잡하다. 그렇지만 복잡한 자취를 따라 부드럽게 움직이는 DIV라는 일반적인 문제에 대한 해결책으로는 아주 훌륭하다.

움직이기 시작할 시간

애니메이션은 많은 형태를 취할 수 있지만 대개 비슷한 모델을 따른다. 하나 이상의 이미지를 만들고 그 이미지들을 일정 속도로 연속적으로 보여주어 마치 한 객체가 부드럽게 움직이는 듯이 보이게 만드는 것이다. 여기에서 배운 테크닉들을 적용하면 메뉴를 스크롤하는 것에서부터 수퍼맨이 화면을 가로질러 나르도록 만드는 것까지 수 많은 효과를 배울 수 있다. 약간의 연습이 필요하긴 하지만 요점을 이해하고 나면 여러분의 상상력에 따라 무한한 가능성이 열려 있다.
데이브 쏘우(Dave Thau)는 인터넷과 관련된 첫 번째 웹 기반 공동체인 bianca.com에서 시작하여 1993년 이후로 인터넷 애플리케이션을 만들고 있다. 그 이후로 자바스크립트 북(The Book of JavaScript)을 집필하였고, Wired Digital사에서 소프트웨어 공학 및 선임 과학자(Software Engineering and Senior Scientist) 부서의 책임자로 활동하였으며 수백명의 아티스트와 엔지니어 그리고 어린이들에게 프로그래밍 언어를 가르쳤다.
TAG :

이전 글 : PC 되살리기

다음 글 : 디지털 권리의 미래

댓글 입력
자료실