반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리

HTML5 Canvas 이용하기 - 04 - Image/Animation

2011. 12. 24. 23:23 | Posted by 솔웅


반응형
HTML 5로 이미지를 다루는 법을 배워보겠습니다.
 function doFirst(){
    var x = document.getElementById('canvas');
    canvas = x.getContext('2d');
   
    var pic = new Image();
    pic.src="nytimes.jpg";
   
    canvas.drawImage(pic, 0, 0);
 }
 window.addEventListener("load",doFirst,false);

곧바로 자바스크립트부터 들어가죠.
처음에 Image를 다룰 변수를 만들어 줍니다. 여기서는 pic이라는 지역변수(var)를 만들었습니다.
그 다음엔 이 변수에 원하는 이미지를 담습니다. 저는 nytimes.jpg라는 이미지를 사용할겁니다. (이 글 쓰면서 뉴욕타임즈 들어가 보니까 이 이미지가 있더라구요.)
이제 캔바스에다 그리면 됩니다. drawImage를 사용해서 pic을 x=0,y=0의 위치에 그립니다.

이렇게 하면 HTML5로 canvas에 image 를 넣을 수 있습니다.

그러면 아래 세 줄을 추가해 보세요.
    canvas.drawImage(pic, 100, 50,150,80);
    canvas.drawImage(pic, 400, 30, 69, 97, 300, 100, 103, 145);
    canvas.clearRect(500, 100, 10, 40);

좀 어지러워 졌죠?
첫 번째 줄은 이미지를 100,,50인 위치에 너비 150 높이 80으로 그리라는 의미입니다.
그리고 두번째는 400,30인 위치에서 너비 69 높이 97 만큼 복사해서 300,100 위치에 너비 103 높이 145 만큼 그리라는 겁니다.
그리니까 이미지 일 부분을 Copy and Paste 까지 할 수 있네요.
마지막은 우리가 전에 배웠던 clearRect 입니다. 해당 부분 만큼을 지워버리는 거죠.

제대로 설명 된 걸 한번 볼께요.




이제 좀 다이나믹한 걸 함 보겠습니다.
 function doFirst(){
    var x = document.getElementById('canvas');
    canvas = x.getContext('2d');

     window.addEventListener("mousemove",dougy,false);
 }
 
 function dougy(e) {
    canvas.clearRect(0,0,600,400);
    var xPos = e.clientX;
    var yPos = e.clientY;
    canvas.fillRect(xPos,yPos,50,50);
 }
 
 window.addEventListener("load",doFirst,false);

doFirst 안에 리스너를 하나 달았습니다. 지금까지 맨 밑에 있는 리스너만 썼었는데요. 이건 load 할 때 시행 되는 겁니다. doFirst 안에 있는 것은 마우스가 움직일 때 계속 불려 지는 겁니다.
마우스가 움직이는 동안 계속 dougy라는 함수를 호출 합니다.
dougy 함수를 보면 e를 인수로 받습니다. 이 e는 리스너로부터 전달되는 정보들 입니다.
처음에는 canvas를 싹 지웁니다.
다음에 보면 e.clientX 라는게 있는데 이건 마우스를 클릭했을 때 그 x 위치 입니다.
그러니까 e.clientY는 마우스의 y 위치 이겠죠. 이 정보들이 바로 아까 본 e 에 다 들어 있는 겁니다.
이 마우스 위치인 x,y 를 각각 xPos, yPos 변수에 대입 시킵니다.
그리고 사각형을 그리는데요. 그 그리는 위치가 바로 마우스의 위치인 xPos,yPos 입니다.
그러니까 마우스가 움직이는 동안 캔버스를 지우고 마우스 위치에 사각형을 그리는 작업을 계속 하는 겁니다.
1초에 몇백번 하는지 몇천번 하는지 모르겠지만 하여간 계속 그 작업을 합니다.
그러면 사각형이 마우스를 움직는대로 따라 움직이게 됩니다.

제법 그럴듯한 애니메이션 효과가 납니다.
이 사각형을 우주선 이미지로 바꾸면 마우스가 움직이는 대로 우주선이 움직일 수도 있습니다.
이제 HTML5로 게임 제작하기에 한발짝 더 다가선 것 같습니다.
자 한가지 여기서 dougy 함수 안에 canvas를 지우는 부분을 없애 보세요. // 로 주석을 다셔도 됩니다.

그리고 나서 마우스를 움직이면 캔버스를 지우지 않기 때문에 이전에 그린 사각형이 그대로 남겠죠? 그러면 위에 보시는 바와 같이 선을 그을 수가 있습니다.
포토샵의 브러시 툴 갖지 않나요?

지금까지 HTML5의 Canvas에 대해 알아봤습니다.
다음엔 HTML5의 새로운 Concept 에 대해 공부하겠습니다.
반응형

HTML5 Canvas 이용하기 - 03 -

2011. 12. 24. 04:05 | Posted by 솔웅


반응형
HTML5 canvas에 대해서 계속 공부하고 있습니다.

오늘은 일단 아래 코드를 공부할께요.
 function doFirst(){
    var x = document.getElementById('canvas');
    canvas = x.getContext('2d');
   
    canvas.font="bold 22px Tahoma";
    canvas.textAlign="start;
    canvas.fillText("start",10,30);
   
    canvas.translate(100,150);
    canvas.fillText("after translate",0,0);
   
    canvas.rotate(1);
    canvas.fillText("after rotate",0,0);
   
    canvas.scale(1.5,4);
    canvas.fillText("after rotate",0,20);
 }
 window.addEventListener("load",doFirst,false);

처음에 font를 정해주고 정렬방식을 start 로 해 줍니다.
(보통 left,center,right 인데 여긴 start, middle, end 인가 봅니다. 왜 그런진 모르겠어요. 만든사람 마음이니까......)
그리고 start라는 글자를 쓰게 되요.
다음줄은 보니까 translate 이 있네요. 이건 x 좌표로 100포인트 y좌표로 150포인트 옮기라는 겁니다.
다음에 after translate 이라는 글자를 쓰는데 x,y는 0,0 입니다. 이 0,0은 canvas의 0,0이 아니라 translate 했던 100,150 을 기준으로 한 0,0 입니다.
그러니까 translate 하지 않고 100,150 한것이랑 똑같죠.
그래서 이 after translate 는 100,150 지점에 쓰여질 겁니다.
이 translate 명령어는 start 글자에는 적용되지 않습니다. translate 명령이 내려진 이후의 애들한테만 적용됩니다.

다음엔 뭐가 있죠? rotate 이 있네요.
이건 회전 시키라는 명령어인데요. 괄호 안에 있는 1은 Radian 단위 입니다. 반지름과 같은 길이의 호를  연결한 원의 중심 각도인데요. 나중에 게임 만들 때 아주 많이 쓰이게 되는 개념입니다. 모르시면 따로 공부해 두시는게 좋을거예요.
하여간 1Radian 만큼 회전시키라고 한 다음에 있는 것이 after rotate 이라는 글자입니다.
이 글자는 일단 translate 명령이 내려진 다음에 있으니까 좌표가 0,0으로 돼 있지만 실제 출력되는 위치는 100,150 지점이 됩니다.
그리고 rotate 하라고 했으니까 1Ratian 만큼 회전할 테구요.
다음은 scale 입니다. 좌우로 1.5배 위아래로 4배 키우라는 겁니다.
그리고 after Scale이라는 글자가 찍힐 텐데..
당연히 이 글자는 translate, rotate, scale 영향을 다 받게 됩니다.


이제 앞으로 캔바스에 그려지는 모든 것들은 다 이렇게 translate,rotate,scale 이 적용될 텐데요.
만약 이게 다 적용되지 않도록 하고 싶으면 어떻게 할까요?
save와 restore를 이용하면 됩니다.
자바스크립트를 아래와 같이 바꿔보세요.
 function doFirst(){
    var x = document.getElementById('canvas');
    canvas = x.getContext('2d');
   
    canvas.font="bold 22px Tahoma";
    canvas.textAlign="start";
    canvas.fillText("start",10,30);
   
    canvas.save();
   
    canvas.translate(100,150);
    canvas.fillText("after translate",0,0);
   
    canvas.rotate(1);
    canvas.fillText("after rotate",0,0);
   
    canvas.scale(1.5,4);
    canvas.fillText("after Scale",0,20);
   
    canvas.restore();
   
    canvas.fillText("after restoring", 110,30);
 }
 window.addEventListener("load",doFirst,false);

보시면 처음 폰트와 정렬이 선언된 다음에 save() 를 해 놓습니다.
그리고 그 이후에 translate,rotate,scale 이런 기능들을 적용하구요.
다시 이 기능들을 적용하지 않기 위해 restore()를 해 줬습니다.
after restoring 은 제대로 110,30 위치에 쓰여질 겁니다.


오늘은 간단한 몇가지 기능들에 대해 공부했습니다.
반응형


반응형
오랜만에 코로나쪽을 공부해 보겠습니다.
얼마전에 기능이 추가 된 화면전환 효과 Story Board 샘플 소스를 분석해 볼께요.

Story Board API는 예전에 살펴 본 바 있습니다.

이 샘플 예제는 Corona SDK/interface 안에 storyboard 라는 폴더에 있습니다.

우선 main.lua 소스 파일 입니다.

display.setStatusBar( display.HiddenStatusBar )
-- require controller module
local storyboard = require "storyboard"
local widget = require "widget"
-- load first scene
storyboard.gotoScene( "scene1", "fade", 400 )
-- Display objects added below will not respond to storyboard transitions
-- table to setup tabBar buttons
local tabButtons = {
    { label="First", up="icon1.png", down="icon1-down.png", width = 32, height = 32, selected=true },
    { label="Second", up="icon2.png", down="icon2-down.png", width = 32, height = 32 },
}
-- create the actual tabBar widget
local tabBar = widget.newTabBar{
    top = display.contentHeight - 50,    -- 50 is default height for tabBar widget
    buttons = tabButtons
}
main.lua에서는 딱 두 부분으로 나뉘어 져 있습니다.
첫번째는 Story Board 를 이용한 부분이고 두번째는 Tab Button을 이용한 부분입니다.
이 두 기능을 이용하기 위해 storyboard와 widget 을 require 했습니다.
그리고 storyboard 부분은 gotoScene 을 이용해서 화면 전환 한 것 밖에는 없습니다.
scene1.lua 로 가는데 0.4초동안 fade 효과를 이용해서 화면 전환을 합니다.
화면 전환 효과는 fade를 포함해서 모두 20개의 효과가 있습니다.
내용은 여기를 클릭하시면 보실 수 있습니다.
두번째는 탭바를 생성하는 건데요. 일단 tabButtons 테이블 안에 탭버튼 두개의 정보들을 담아 놓습니다. 그리고 widget.newTabBar로 Tabbar를 만드는데요. 중요한건 이 탭 바는 화면전환해도 계속 남아 있는 다는 겁니다.

Story Board API 에서는 scene 의 life cycle 메소드가 4개 있습니다.
createScene,enterScene,exitScene,destroyScene
그리고 이 메소드 안에서 생성한 object 들만 화면전환시 적용이 됩니다.
director.lua 클래스는 localGroup을 생성해서 그 그룹에 insert 한 객체들만 적용이 됐었습니다.
이번에 코로나에서 제공한 화면전환 API는 이보다 더 세밀한 화면 전환 단계를 이용할 수 있게 만들었습니다.
이 4개 메소드가 적용된 예는 Scene1 설명할 때 나올겁니다.

일단 여기서 만드는 tabbar 는 화면전환시 적용되지 않고 처음부터 끝까지 계속 남아있을 겁니다.
(그리고 tab bar 관련된 글을 한번도 안 다뤄봤는데요. 이 기능은 나중에 자세히 다뤄볼께요.)


-- storyboard.gotoScene( "scene1", "fade", 400 )
이렇게 scene1.lua로 넘어가는 부분을 주석 걸어놓고 실행하면 위 화면이 나옵니다.

이게 온전히 main.lua만을 실행했을 때 나오는 화면입니다.
그 다음에 scene1.lua를 보겠습니다.

local storyboard = require( "storyboard" )
local scene = storyboard.newScene()

---------------------------------------------------------------------------------
-- BEGINNING OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

local image, text1, text2, text3, memTimer

-- Touch event listener for background image
local function onSceneTouch( self, event )
    if event.phase == "began" then
        storyboard.gotoScene( "scene2", "slideLeft", 800  )
        return true
    end
end

-- Called when the scene's view does not exist:
function scene:createScene( event )
    local screenGroup = self.view
   
    image = display.newImage( "bg.jpg" )
    screenGroup:insert( image )
   
    image.touch = onSceneTouch
   
    text1 = display.newText( "Scene 1", 0, 0, native.systemFontBold, 24 )
    text1:setTextColor( 255 )
    text1:setReferencePoint( display.CenterReferencePoint )
    text1.x, text1.y = display.contentWidth * 0.5, 50
    screenGroup:insert( text1 )
   
    text2 = display.newText( "MemUsage: ", 0, 0, native.systemFont, 16 )
    text2:setTextColor( 255 )
    text2:setReferencePoint( display.CenterReferencePoint )
    text2.x, text2.y = display.contentWidth * 0.5, display.contentHeight * 0.5
    screenGroup:insert( text2 )
   
    text3 = display.newText( "Touch to continue.", 0, 0, native.systemFontBold, 18 )
    text3:setTextColor( 255 ); text3.isVisible = false
    text3:setReferencePoint( display.CenterReferencePoint )
    text3.x, text3.y = display.contentWidth * 0.5, display.contentHeight - 100
    screenGroup:insert( text3 )
   
    print( "\n1: createScene event")
end

-- Called immediately after scene has moved onscreen:
function scene:enterScene( event )
   
    print( "1: enterScene event" )
   
    -- remove previous scene's view
    storyboard.purgeScene( "scene4" )
   
    -- Update Lua memory text display
    local showMem = function()
        image:addEventListener( "touch", image )
        text3.isVisible = true
        text2.text = text2.text .. collectgarbage("count")/1000 .. "MB"
        text2.x = display.contentWidth * 0.5
    end
    memTimer = timer.performWithDelay( 1000, showMem, 1 )
end

-- Called when scene is about to move offscreen:
function scene:exitScene( event )
   
    print( "1: exitScene event" )
   
    -- remove touch listener for image
    image:removeEventListener( "touch", image )
   
    -- cancel timer
    timer.cancel( memTimer ); memTimer = nil;
   
    -- reset label text
    text2.text = "MemUsage: "
end


-- Called prior to the removal of scene's "view" (display group)
function scene:destroyScene( event )
   
    print( "((destroying scene 1's view))" )
end

---------------------------------------------------------------------------------
-- END OF YOUR IMPLEMENTATION
---------------------------------------------------------------------------------

-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )

-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )

-- "exitScene" event is dispatched before next scene's transition begins
scene:addEventListener( "exitScene", scene )

-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )

---------------------------------------------------------------------------------

return scene

이 scene1.lua 는 onSceneTouch 함수와 StoryBoard 에 소속된 4개 함수로 구성된 간단한 구조입니다

이 함수 호출은 Storyboard 4인방은 scene:addEventListener 를 사용해서 호출이 되고 onSceneTouch 는 배경이미지에 리스너를 달아서 화면을 터치하면 scene2로 이동하게끔 하는 겁니다.

4인방 메소드에서는 self.view를 이용해서 객체들을 그룹화 하면서 화면에 보여줄 수 있도록 합니다.

처음 createScene 에서는 배경이미지와 텍스트 등 기본 display 요소들을 정의하고 표시합니다.

enterScene 에서는  0.1초 후에 실행되는 타이머를 달아서 showMem 함수를 실행하도록 했습니다.
이 함수는 배경이미지를 터치하면 scene2.lua로 가도록 하는 onSceneTouch 함수를 Call 하는 리스너를 달았고 현재 메모리를 모여주는 로직이 있습니다.
그 이전에 storyboard.purgeScene( "scene4" ) 이 있는데요. purgeScene 은 해당 scene(여기서는 scene4) 에 있는 객체들을 모두 unload 합니다. purge말고 remove를 사용하면 unload 될 뿐만 아니라 메모리에서도 사라집니다.
이 기능은 메모리를 절약하거나 할 때 사용하는 건데 scene1에서 scene2로 넘어 갈때 터미널에 뭐가 찍히는지 보면 잘 알 수 있습니다.

보시다시피 scene1이 시작되면 scene4 의 destroy 가 호출 되서 그 안의 내용이 실행 됩니다.
scene1에서 scene2로 넘어갈 때 destroyScene 함수가 호출되는게 아니라 해당 scene이 unload 될 때 destroyScene 이 호출 됩니다.
그리고 purgeScene 하게 되면 해당 scene 의 destroyScene 이 호출 됩니다.
removeScene 하게 되면 purgeScene 한 후 메모리에서 없애므로 자연히 destroyScene 이 호출 됩니다.

exitScene 은 다음 화면으로 넘어갈 때 실행 되는데 여기서는 모든 리스너를 remove 하고 timer 도 cancel 합니다.

여기까지 하면 실행되는 화면이 아래와 같습니다.


나머지 scene2,scene3,scene4 도 이 scene1과 구조가 동일 합니다.

그런데 여기서 드는 한가지 의문점은 Tab Bar 는 화면 전환에 상관 없이 계속 있는데요.

특정 화면에서 이 TabBar 를 안 보이게 하거나 없애거나 아니면 어떻게 콘트롤을 할 수 있을까요?

그렇게 하려면 main.lua 에서 tabBar 를 local 이 아니라 glabal 변수로 선언하면 됩니다. 그냥 local 이란 글자만 지우세요.

그리고 scene3.lua 맨 밑에 (return scene 바로 윗줄에) tabBar.isVisible = false; 를 넣어보시고. scene4.lua에는 tabBar.isVisible = true 를 넣어보세요.

그러면 이렇게 scene3에는 탭바가 없어지고 scene4에는 나타나게 됩니다.
이런식으로 tabbar를 제어하시면 됩니다.


반응형