21.Graphics

21.1Graphics 컴포넌트 소개

Graphics 컴포넌트는 선이나 도형을 그릴 수 있는 기능을 제공합니다. Graphics 컴포넌트는 HTML5 Canvas 요소를 마치 다른 컴포넌트처럼 개발자가 익숙하게 사용할 수 있습니다. 컴포넌트의 속성, 메소드, 이벤트를 사용해 스크립트 상에서 쉽게 원하는 이미지를 만들 수 있습니다.

Graphics 컴포넌트는 17.1.0.100 버전부터 지원합니다.

21.1.1Graphics.json 등록하기

넥사크로 스튜디오 설치 시 Graphics 컴포넌트는 기본 설정되지 않은 상태입니다. 프로젝트 TypeDefinition 에서 Graphics.json를 등록하고 Graphics 컴포넌트를 오브젝트로 설정해주어야 합니다.

1

새로운 프로젝트를 만들거나 기존 프로젝트를 실행합니다.

2

Project Explorer에서 TypeDefinition 항목 아래에 Objects 항목을 선택하고 컨텍스트 메뉴에서 [Edit]를 선택합니다.

3

Modules 항목에서 [+] 버튼을 클릭하고 탐색기에서 Graphics.json 파일을 선택합니다.

Graphics.json 파일은 아래 경로에서 확인할 수 있습니다.

[넥사크로 스튜디오 설치 폴더]\nexacro17lib\component\Graphics.json

4

Modules 항목에 Graphics.json이 등록된 것을 확인하고 nexacro.Graphics 컴포넌트를 Objects 목록에 추가합니다.

21.2선 그리기

Graphics 컴포넌트 위에 선을 그리는 가장 기본적인 동작을 살펴봅니다.

21.2.1예제

빈 화면에 아래와 같이 빨간색 선을 그립니다.

sample_graphics_01.xfdl

21.2.2예제에서 사용한 핵심 기능

strokepen

선의 색이나 형식을 지정합니다. default 값이 없으므로 해당 속성값을 지정하지 않으면 화면에 선이 표시되지 않습니다.

21.2.3예제 구현 방법

1

Graphics 컴포넌트를 화면에 배치합니다.

2

Form의 onload 이벤트 함수를 추가합니다.

Graphics 컴포넌트가 로딩되고 그 위에 선이 그려질 수 있도록 스크립트를 추가합니다.

3

onload 이벤트 함수의 스크립트를 아래와 같이 작성합니다.

var objGLine = new nexacro.GraphicsLine();
this.Graphics00.addChild( "GraphicsLine00", objGLine );
objGLine.set_x(10);
objGLine.set_y(10);    
objGLine.set_x2(50);
objGLine.set_y2(50);
objGLine.set_strokepen('1px solid red');
this.Graphics00.redraw();

Graphics 컴포넌트 위에 필요한 컨트롤(선이나 도형)을 자식으로 등록하는 방식입니다. 예제에서는 선을 추가할 것이기 때문에 GraphicsLine 오브젝트를 생성하고 Graphics 컴포넌트의 addChild 메소드를 사용해 추가합니다.

var objGLine = new nexacro.GraphicsLine(); 
this.Graphics00.addChild( "GraphicsLine00", objGLine );

GraphicsLine 오브젝트의 속성을 지정해서 시작점(x, y)와 끝나는점(x2, y2)를 지정하고 선의 굵기와 색상(strokepen)을 설정합니다. 지정된 속성으로 그려진 선이나 도형을 표시하려면 Graphics 컴포넌트의 redraw 메소드를 호출해주어야 합니다.

this.Graphics00.redraw();

4

QuickView(Ctrl + F6)로 실행한 후 선이 표시되는지 확인합니다.

21.3선이나 도형을 그룹으로 묶어서 처리하기

개별적인 선이나 도형을 그리는 오브젝트를 Graphics 컴포넌트에 직접 추가할 수도 있습니다. 하지만, 이렇게 구성을 한 경우에는 전체 도형의 위치를 변경해야 할 경우 개별 오브젝트의 메소드를 매번 호출해야 하는 불편함이 있습니다.

GraphicsGroup 오브젝트를 사용하면 Graphics 컴포넌트에는 GraphicsGroup 오브젝트만 추가하고 나머지 오브젝트는 GraphicsGroup 오브젝트의 자식으로 추가합니다. 이렇게 하면 GraphicsGroup 오브젝트만 이동하게 되면 나머지 도형이 같이 따라 움직이게 됩니다.

21.3.1예제

[Add Objects] 버튼을 클릭하면 여러 형태의 오브젝트를 표시합니다. 사각형은 Graphics 컴포넌트의 자식으로 추가했고, 나머지는 GraphicsGroup 오브젝트의 자식으로 추가했습니다. [rotate] 버튼 클릭 시 사각형과 나머지 도형이 어떻게 움직이는지 확인할 수 있습니다.

sample_graphics_02.xfdl

21.3.2예제에서 사용한 핵심 기능

setImageLoadEventHandler

GraphicsImage 오브젝트 사용 시 이미지 로딩 후 처리가 필요한 부분이 있을 때 사용합니다. 예제에서는 이미지의 크기(width, height)를 이미지 로딩 후 실제 이미지 값에서 가져오도록 처리했습니다.

getObjectByID

Graphics 컴포넌트, GraphicsGroup 오브젝트, GraphicsPaths 오브젝트의 자식을 ID 값으로 찾습니다. 예제에서는 Graphics 컴포넌트를 기준으로 찾았습니다. 예제처럼 Graphics 컴포넌트를 기준으로 찾을 수도 있고 GraphicsGroup 오브젝트를 기준으로 찾을 수도 있습니다.

21.3.3예제 구현 방법

1

Graphics 컴포넌트를 화면에 배치합니다.

2

Button 컴포넌트 3개를 Graphics 컴포넌트 옆에 배치합니다.

3

첫 번째 Button 컴포넌트의 onclick 이벤트 함수를 작성합니다.

this.Button01_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo)
{
    this.addGraphicsControl();
    this.Graphics00.redraw();
};

4

addGraphicsControl 함수의 스크립트를 아래와 같이 작성합니다.

this.addGraphicsControl = function()
{
    //GraphicsRect
    var objGRect = new nexacro.GraphicsRect();
    this.Graphics00.addChild( "GraphicsRect00", objGRect );
    objGRect.set_x(50);
    objGRect.set_y(50);
    objGRect.set_width(50);
    objGRect.set_height(50);
    objGRect.set_strokepen("1px solid red");
    
    // GraphicsEllipse
    var objGEllipse = new nexacro.GraphicsEllipse();
    objGEllipse.set_x(150);
    objGEllipse.set_y(75);
    objGEllipse.set_width(50);
    objGEllipse.set_height(50);
    objGEllipse.set_strokepen("1px solid red");    
    
    // GraphicsText
    var objGText = new nexacro.GraphicsText();
    objGText.set_x(300);
    objGText.set_y(50);
    objGText.set_color('red');
    objGText.set_font('12pt/normal Verdana'); 
    objGText.set_text('Ryan');
    
    // GraphicsImage
    var objGImage = new nexacro.GraphicsImage();
    objGImage.set_x(200);
    objGImage.set_y(50);    
    objGImage.set_image("url('imagerc::img_50.png')");    
    objGImage.setImageLoadEventHandler(this.fn_checkGImage, this);
    
    // GraphicsLine
    var objGLine = new nexacro.GraphicsLine();
    objGLine.set_x(50);
    objGLine.set_y(120);
    objGLine.set_x2(250);
    objGLine.set_y2(0);
    objGLine.set_strokepen('1px solid red');  
    
    // GraphicsPaths
    var objGPaths = new nexacro.GraphicsPaths();
    objGPaths.set_x(50);
    objGPaths.set_y(120);
    objGPaths.set_strokepen('1px solid blue');  

    
    var objGPath = new nexacro.GraphicsPath();    
    objGPaths.addChild( "GraphicsPath00", objGPath );
    objGPath.moveTo(0,0);
    objGPath.arcTo( 50, 50, 50, 100, 0, true, false );
    
    // GraphicsGroup 
    var objGGroup = new nexacro.GraphicsGroup();
    this.Graphics00.addChild( "GraphicsGroup00", objGGroup );
    objGGroup.addChild("GraphicsEllipse00", objGEllipse);
    objGGroup.addChild("GraphicsText00", objGText);
    objGGroup.addChild("GraphicsImage00", objGImage);
    objGGroup.addChild("GraphicsLine00", objGLine);
    objGGroup.addChild("GraphicsPaths00", objGPaths);
}

GraphicsRect 오브젝트는 Graphics 컴포넌트의 자식으로 직접 추가합니다. 하지만, 나머지 오브젝트는 Graphics 컴포넌트의 자식으로 추가하지 않습니다.

this.Graphics00.addChild( "GraphicsRect00", objGRect );

GraphicsImage 오브젝트의 width, height 속성값은 직접 지정할 수도 있지만, 실제 이미지의 값으로 설정하고자 한다면 아래와 같이 이미지가 로딩되고 난 이후에 별도 함수에서 속성값을 처리하도록 합니다.

var objGImage = new nexacro.GraphicsImage();
objGImage.set_image("url('imagerc::img_50.png')");    
objGImage.setImageLoadEventHandler(this.fn_checkGImage, this);

GraphicsGroup 오브젝트를 추가하고 Graphics 컴포넌트의 자식으로 추가합니다. 나머지 도형을 처리하는 오브젝트는 GraphicsGroup 오브젝트의 자식으로 추가합니다.

var objGGroup = new nexacro.GraphicsGroup();
this.Graphics00.addChild( "GraphicsGroup00", objGGroup );
objGGroup.addChild("GraphicsEllipse00", objGEllipse);
objGGroup.addChild("GraphicsText00", objGText);
...

5

GraphicsImage 오브젝트에서 이미지 로딩 후 호출하는 함수의 스크립트를 작성합니다.

이미지 로딩이 완료되는 시점에 처리되기 때문에 redraw 메소드를 실행합니다.

this.fn_checkGImage = function()
{
    var objGImage = this.Graphics00.getObjectByID("GraphicsImage00");
    objGImage.set_width(objGImage.imagewidth);
    objGImage.set_height(objGImage.imageheight);
    this.Graphics00.redraw();
}

6

두 번째 Button 컴포넌트의 onclick 이벤트 함수를 작성합니다.

GraphicsRect 오브젝트만 찾아서 rotate 메소드를 실행합니다.

this.Button02_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo)
{
    var objGRect = this.Graphics00.getObjectByID("GraphicsRect00");
    objGRect.set_fillstyle("yellow");
    objGRect.rotate(10);
    this.Graphics00.redraw();
};

7

세 번째 Button 컴포넌트의 onclick 이벤트 함수를 작성합니다.

GraphicsGroup 오브젝트만 찾아서 rotate 메소드를 실행합니다.

this.Button03_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo)
{
    var objGGroup = this.Graphics00.getObjectByID("GraphicsGroup00");
    objGGroup.rotate(10);
    this.Graphics00.redraw();
};

8

QuickView(Ctrl + F6)로 실행한 후 각 버튼을 클릭해서 도형이 어떻게 반응하는지 확인합니다.

21.4간단한 그림판 만들기

그림판처럼 선을 그리거나 도형을 추가할 수 있는 기능을 구현해봅니다.

스크립트 코드는 공개된 자료를 참고했습니다.

https://icechou.tistory.com/158

21.4.1예제

빈 화면에 Line, Rectangle, Circle 중 하나의 항목을 선택하고 마우스로 드래그하면 도형이 표시됩니다. [clear] 버튼을 클릭하면 화면에 그려진 도형이 모두 사라집니다.

sample_graphics_03.xfdl

21.4.2예제에서 사용한 핵심 기능

clear

모든 자식 오브젝트를 초기화합니다. clear 메소드 실행 후 redraw 메소드를 호출해주어야 실제 화면에서 초기화가 처리됩니다.

21.4.3예제 구현 방법

1

Graphics 컴포넌트를 화면에 배치합니다.

2

Radio 컴포넌트와 Button 컴포넌트를 Graphics 컴포넌트 위에 배치합니다.

Radio 컴포넌트의 innerdataset 항목으로 Line, Rectangle, Circle 항목을 설정합니다.

3

상태값을 처리할 변수를 선언하고 Graphics 컴포넌트에서 마우스 상태에 따른 이벤트 함수를 추가합니다.

Graphics 컴포넌트 위에서 마우스 버튼이 눌려진 시점(onlbuttondown)에 Radio 컴포넌트의 값을 체크해서 어떤 동작을 할지 결정하고 버튼이 눌려진 상태에서 마우스가 움직이면(onmousemove) 도형에 따라 그리기 동작을 처리합니다. 마우스 버튼을 떼면(onlbuttonup) 상태값을 초기화합니다.

var bMouseButtonPressed = false;
var preClientX;
var preClientY;
var nNUm = 0;
var temprectangle;
var tempellipse;
var drawType;
this.Graphics00_onlbuttondown = function(obj:nexacro.Graphics,e:nexacro.MouseEventInfo)
{
    bMouseButtonPressed = true;
    preClientX = e.clientx;
    preClientY = e.clienty;
    drawType = this.Radio00.value;
};

this.Graphics00_onlbuttonup = function(obj:nexacro.Graphics,e:nexacro.MouseEventInfo)
{
    bMouseButtonPressed = false;
    temprectangle = null;
    tempellipse = null;
};

this.Graphics00_onmousemove = function(obj:nexacro.Graphics,e:nexacro.MouseEventInfo)
{
    switch(drawType) {
    case 'Line':
        this.fn_drawLine(e);
        break;
    case 'Rectangle':
        this.fn_drawRect(e);
        break;
    case 'Circle':
        this.fn_drawEllipse(e);
        break;        
    default:
    }
};

4

선을 그리기 위한 스크립트를 아래와 같이 작성합니다.

this.fn_drawLine = function(e:nexacro.MouseEventInfo)
{
    var nowClientX = e.clientx;
    var nowCLientY = e.clienty;
    if(bMouseButtonPressed)
    {
        var objGLine = new nexacro.GraphicsLine();
        objGLine.set_x1(preClientX);
        objGLine.set_x2(nowClientX);
        objGLine.set_y1(preClientY);
        objGLine.set_y2(nowCLientY);
        objGLine.set_strokepen('1px solid red'); 
        this.Graphics00.addChild( "GraphicsControl_"+nNUm, objGLine );
        this.Graphics00.redraw();
        
        preClientX = nowClientX;
        preClientY = nowCLientY;
        nNUm++;
    }
};

Graphics 컴포넌트의 onmousemove 이벤트가 발생할 때마다 새로운 GraphicsLine 오브젝트를 생성하고 선을 이어 그려줍니다.

예를 들어 색상 코드값을 랜덤으로 처리해서 strokepen 속성값을 지정했다면 아래와 같은 색으로 선이 그려집니다. 하나의 선이 아니라 여러 개의 선이 이어져서 하나의 선처럼 보이는 상태입니다.

var colorCode = "#"+Math.round(Math.random()*0xffffff).toString(16);
objGLine.set_strokepen('1px solid '+colorCode);

5

사각형을 그리기 위한 스크립트를 아래와 같이 작성합니다.

this.fn_drawRect = function(e:nexacro.MouseEventInfo)
{
    var nowClientX = e.clientx;
    var nowCLientY = e.clienty;
    if(bMouseButtonPressed)
    {
        var objGRect = new nexacro.GraphicsRect();
        var left = preClientX;
        var top = preClientY;
        var width = nowClientX - preClientX;
        var height = nowCLientY - preClientY;
        
        if(nowClientX < preClientX)
        {
            left = nowClientX;
            width *= -1;
        }
        if(nowCLientY < preClientY)
        {
            top = nowCLientY;
            height *= -1;
        }
 
        objGRect.set_x(left);
        objGRect.set_y(top);
        objGRect.set_width(width);
        objGRect.set_height(height);
        objGRect.set_strokepen('1px solid red'); 
        if(temprectangle)
        {
            this.Graphics00.removeChild(temprectangle);
        }
        temprectangle = "GraphicsControl_"+nNUm;
        this.Graphics00.addChild("GraphicsControl_"+nNUm, objGRect );
        this.Graphics00.redraw();

        nNUm++;
    }
};

마우스 포인터가 시작점에서 오른쪽 아래 방향이 아니라 왼쪽이나 위로 움직이는 경우 시작점을 보정해주는 코드를 추가했습니다.

if(nowClientX < preClientX)
{
    left = nowClientX;
    width *= -1;
}

그리고 마우스 포인터가 움직일 때 임시로 사각형을 표시해주고 삭제합니다. 이렇게 하면 최종적으로 사각형이 그려질 위치를 사용자가 알 수 있습니다.

if(temprectangle)
{
    this.Graphics00.removeChild(temprectangle);
}

6

원을 그리기 위한 스크립트를 아래와 같이 작성합니다.

this.fn_drawEllipse = function(e:nexacro.MouseEventInfo)
{
    var nowClientX = e.clientx;
    var nowCLientY = e.clienty;
    if(bMouseButtonPressed)
    {
        var objGEllipse = new nexacro.GraphicsEllipse();
        var left = preClientX;
        var top = preClientY;
        var width = nowClientX - preClientX;
        var height = nowCLientY - preClientY;
        
        if(nowClientX < preClientX)
        {
            left = nowClientX;
            width *= -1;
        }
        if(nowCLientY < preClientY)
        {
            top = nowCLientY;
            height *= -1;
        }
 
        objGEllipse.set_x(left+(width/2));
        objGEllipse.set_y(top+(height/2));
        objGEllipse.set_width(width);
        objGEllipse.set_height(height);
        objGEllipse.set_strokepen('1px solid red'); 
        if(tempellipse)
        {
            this.Graphics00.removeChild(tempellipse);
        }
        tempellipse = "GraphicsControl_"+nNUm;
        this.Graphics00.addChild("GraphicsControl_"+nNUm, objGEllipse );
        this.Graphics00.redraw();

        nNUm++;
    }
};

7

[clear] 버튼 클릭 시 onclick 이벤트 함수를 작성합니다.

this.Button00_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo)
{
    this.Graphics00.clear();
    this.Graphics00.redraw();
};

8

QuickView(Ctrl + F6)로 실행한 후 그림판 위에 자유롭게 그림이나 도형을 그려봅니다.