UI Laboratory

UI 개발을 위한 레퍼런스

INDEX

코드 재사용 및 최적화


직접 만드는 코드 프로파일링

자바스크립트 최적화

| 룩업 테이블

룩업 테이블에는 비용이 비싼 연산의 미리 계산된 값을 저장할 수 있다. 그 후 정수 색인 번호를 사용해 룩업 테이블에서 값을 빠르게 끌어올 수 있다. 룩업 테이블을 통해 값에 접근하면 직접 계산하는 것보다 더 적은 비용으로 연산하므로 프로그램의 성능이 더 좋아진다.

DHTML 기초

DHTML 스프라이트 만들기

컴퓨터 그래픽에서 스프라이트(sprites)는 소프트웨어로 제어하여 움직이는 2차원 비트맵 객체를 뜻한다.

| 이미지 애니메이션

CSS background-position 속성을 사용하면 큰 이미지의 일부 영역만 HTML 요소에서 보여줄 수 있다. 이러한 스프라이트 이미지를 사용하려면 div에서 background-position 속성을 사용하여 너비와 높이를 정하듯 수직, 수평 위치를 정해주고 간단한 색인 번호로 스프라이트 이미지를 참조할 수 있다.

색인 번호를 참조하여 이미지 안에서의 픽셀위치를 구한다. 아래는 색인번호를 이용해서 수직과 수평 픽셀을 구하는 계산법이다.

위 이미지의 너비는 256픽셀이고 각 스프라이트 이미지(맨 아래 이미지는 제외)는 가로, 세로 64 픽셀의 사각형에 들어있다. 자바스크립트로 픽셀의 위치는 다음과 같이 계산할 수 있다.

			var index = 3;
			var vertOffset = -Math.floor(index*64/256)*64;
			var horizOffset = -(index*64%256);
			alert('x :' + horizOffset + ', y : ' + vertOffset);
		

위 계산은 index가 3인 네번째 스프라이프 이미지의 x와 y 좌표 (큰 이미지에서의)를 구한다.

맨 아래에 위치한 32x32 픽셀짜리 작은 스프라이프 이미지는 아래와 같이 계산한다.

			var index = 32;
			var vertOffset = -Math.floor(index*32/256)*32;
			var horizOffset = -(index*32%256);
			alert('x :' + horizOffset + ', y : ' + vertOffset);
		

| 스프라이트 코드

설정 인수를 포함하고 있는 params라는 객체를 DHTMLSprite 객체에 인수로 전달한다. 이 방식을 사용하면 DHTMLSprite로부터 객체를 상속받아 params에 설정값을 간단하게 추가할 수 있다.

			var DHTMLSprite = function(params){
				...
			};
			
			var params = {
				images : 'images/sprites.png',	//이미지 파일의 경로
				imagesWidth: 256,				//이미지 파일의 픽셀너비
				width: 64,						//스프라이트의 너비
				height: 64,						//스프라이트의 높이
				$drawTarget: $('#draw-target')	//스프라이트 div를 추가할 부모 요소
			};
		

params 속성의 값을 지역 변수에 복사하면 지역변수를 통해 인수에 접근하는 것이 params 객체의 속성으로 접근할 때보다 더 빠르게 접근할 수 있다.

			var width = params.width,
				height = params.height,
				imagesWidth = params.imagesWidth,
		

다음으로 스프라이트 div요소를 params.$drawTarget에 참조된 DOM 요소에 추가한 후 스프라이트 div는 $element로 참조한다. 스프라이트 div의 style 속성에 대한 참조는 elemStyle에 저장하여 CSS 속성을 변경할 때 최적화된 성능을 낼 수 있다.

			$element = params.$drawTarget.append ('<div />').find(':last'),
			elemStyle = $element[0].style,

			// Math.floor 함수에 더 빠르게 접근하기 위해 지역변수로 참조한다.
			mathFloor = Math.floor;
		

다음은 스프라이트 div 요소에 대한 초기 설정을 css()함수를 사용하여 설정한다.

			$element.css({
				position: 'absolute',
				width: width,
				height: height,
				backgroundImage : 'url('+ params.images +')'
			});
		

이젠 스프라이트 메소드를 포함하고자 객체를 만들고 that 변수에 저장한다. 클로저를 만든 that 객체는 이제 DHTMLSprite 함수 바깥에서도 내부에 정의했던 변수에 언제든 접근할 수 있다.

			var that = {
				
				//스프라이트 div 요소의 위치를 업데이트하는 메소드
				draw : function(x,y){
					elemStyle.left = x + 'px';
					elemStyle.top = y + 'px';
				},
				//표시할 스프라이트 이미지를 교체하는 메소드
				changeImage: function(index){
					index *= width;
					var vOffset = -mathFloor(index/imagesWidth)*height;
					var hOffset = -index % imagesWidth;
					elemStyle.backgroundPosition = hOffset + 'px ' + vOffset + 'px';
				},
				//스프라이트 div요소를 보여준다.
				show: function(){
					elemStyle.display = 'block';
				},
				//스프라이트 div요소를 숨긴다.
				hide: function(){
					elemStyle.display = 'none';
				},
				//스프라이트 div요소를 제거한다.
				destroy: function(){
					$element.remove();				
				}
			};
			
			//DHTMLSprite의 인스턴스를 반환한다.
			return that;
		

아래는 전체 소스코드와 실행화면이다.

| 동적인 스프라이트 애니메이션

bouncySprite라는 새로운 객체를 만들어 페이지 가장자리에서 튕기는 DHTMLSprite를 생성한다. bouncySprite가 DHTMLSprite를 상속하고 확장하여 새로운 기능을 추가하도록 한다.

			var bouncySprite = function(params){
				var x = params.x,			// x 좌표의 픽셀값
					y = params.y,			// y 좌표의 픽셀값
					xDir = params.xDir,		// x축 이동 방향
					yDir = params.yDir,		// y축 이동 방향
					maxX = params.maxX,		// x좌표의 최대값
					maxY = params.maxY,		// y좌표의 최대값
					animIndex = 0;			// 현재 애니메이션 이미지의 색인

					...
			}
		

이제 DHTMLSprite 객체를 만들고 참조를 that 변수에 저장한다. params 객체에는 설정값이 포함되어 있다.

			var that = DHTMLSprite(params);
		

이제 MoveAndDraw 메소드를 추가해서 that에 저장된 DHTMLSprite 인스턴스의 참조를 확장한다. 이 방법이 bouncySprite 인스턴스를 만드는 방법보다 더 효율적이다.

			that.moveAndDraw = function(){
				x += xDir;		//스프라이트의 x좌표에 xDir 변수의 값을 추가해 스프라이트를 x방향으로 이동
				y += yDir;		//스프라이트의 y좌표에 yDir 변수의 값을 추가해 스프라이트를 y방향으로 이동
		

animIndex 변수의 값은 xDir에 따라 증가시키거나 감소시킬 것이며 나머지 연산자(%)를 사용해 이 값이 항상 -4부터 +4까지의 값이 되도록 만든다. 만약 animIndex의 값이 음수라면 값을 보정하고 해당하는 양수 색인 번호로 바꾼다.

			animIndex += xDir>0 ? 1 : -1;
			animIndex %= 5;  
			animIndex += animIndex<0 ? 5 : 0;
		

다음으로 bouncySprite가 maxX와 maxY로 정의된 영역을 벗어났는지 확인하고 만약 벗어났다면 이동 방향(xDir, yDir)을 반대로 바꾸어 '튕기는'것 처럼 보이게 한다.

			if((xDir<0 && x<0) || (xDir>maxX && x>=maxX)){
				xDir = -xDir;
			}
			if((yDir<0 && y<0) || (yDir>maxY && y>=maxY)){
				yDir = -yDir;
			}
		

bouncySprite의 애니메이션 색인을 업데이트하고 스프라이트를 새 위치에 그린다.

			that.changeImage(animIndex);
			that.draw(x,y);
		

프로그램에서 사용할 수 있도록 bouncySprite 객체의 인스턴스를 참조하고 있는 that을 반환한다.

			return that;
		

이제 여러개의 bouncySprite 객체를 만들고 다룰 수 있도록 또 다른 객체를 만든다. 이 객체를 bouncyBoss라 이름을 붙이고 두개의 인수를 전달한다.

			// numBouncy는 생성할 bouncySprite의 개수
			// $drawTarget은 만들어진 bouncySprite 객체들을 추가할 부모 요소
			var bouncyBoss = function(numBouncy, $drawTarget){
		

bouncySprite 객체는 필요한 만큼 만들어진 후 bouncys 배열에 추가된다. 또한, 만들어진 bouncySprite에는 시작 위치와 이동 방향(xDir과 yDir)이 임의로 정해진다. $drawTarget의 최대 영역도 전달된다.

			var bouncys = [];
			for(var i=0;i<numBouncy;i++){
				bouncys.push(bouncySprite({
					images : 'images/sprites.png',
					imagesWidth: 256,
					width: 64,
					height: 64,
					$drawTarget: $drawTarget,
					x: Math.random() * ($drawTarget.width()-64),
					y: Math.random() * ($drawTarget.height()-64),
					xDir: Math.random()*4-2,
					yDir: Math.random()*4-2,
					maxX: $drawTarget.width()-64,
					maxY: $drawTarget.height()-64
				}));
			}
		

moveAll 메소드는 bouncys 배열에 저장된 모든 bouncySprite들의 moveAndDraw 메소드를 호출한다. 모든 객체를 이동시키고 나면 setTimeout을 만들어서 자기 자신을 다시 호출한다.

			var moveAll = function(){
				var len = bouncys.length;
				for(i=0;i<len;i++){
					bouncys[i].moveAndDraw();
				}
				setTimeout(moveAll,10);
			}
			// moveAll() 함수를 호출하여 애니메이션을 시작한다.
			moveAll();