===== 목차 =====
* 개요
* 업데이트 요약
* 구현: 스타일시트 동적 로딩
* 구현: SVG 벡터 객체의 추적 및 다루기
* 구현; 오브젝트 필터
* 구현: 키프레임 설명자를 이용한 회전 효과
* 구현: 프리셋을 이용한 조건에 따른 효과 변화
* 구현: 브라우저에서 표시되는 벡터 객체의 축좌표 계산
* 구현: 다중 패널 지원
* 적용: 컴파일 및 설치 방법
==== 개요 ====
본 문서는 모니터링 대시보드에서 SVG를 다루기 위한 추가 개발에 고려된 기술적 내용을 담았다.
==== 업데이트 요약 ====
* 오브젝트 필터 적용: 객체 클래스에 'thing'이 적용된 객체만 리스트로 보냄.
* 역방향 회전 적용: 패널 옵션에서 각 객체 항목의 'Reverse' 체크박스 선택.
* 다중 패널 지원: 다중 패널을 추가해도 정상적으로 동작하도록 개선.
* SVG 개발 문서 작성: 객체 축좌표 계산, 애니메이션 키프레임 설명자 등의 내용이 담김.
==== 구현 상세 ====
* 구현: 스타일시트 동적 로딩
CSS3에서 지원하는 스펙 사항을 플러그인에서 활용하기 위해 스타일시트(CSS)를 동적으로 요청할 필요성이 있다. jQuery를 이용하는 아래 구문으로 CSS의 동적 로드가 가능하다.
$("", {
rel: "stylesheet",
type: "text/css",
href: this.panel.assets.basePath + '/' + 'style.spin.css?' + this.createRandomId('', 10)
}).appendTo("head");
CSS를 동적으로 로드해야 할 시에는 브라우저에 캐시로 남지 않도록 (만료 기간을 1회성으로 하기 위해) 랜덤 값 생성 함수를 같이 이용하여야 한다. 아래와 같은 랜덤 함수를 사용하면 된다. 랜덤 함수는 플러그인에서 상당히 많은 부분에 중요한 역할을 한다.
createRandomId(prefix, size) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < size; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return (prefix + text);
}
* 구현: SVG 벡터 객체의 추적 및 다루기
SVG 벡터 이미지는 XML 형식을 따르고 있다. SVG 벡터 이미지의 원활한 제어를 위해서는 SnapSVG-JS와 jQuery의 적절한 병행 이용이 필요하다.
var els = [];
var svg = Snap("div." + ctrl.panel.canvasname).select("svg");
var gs = svg.selectAll('*');
gs.forEach(function(el) {
var $xml = $(el.toString()),
$elmid = $xml.eq(0).attr("id"),
$elmHasCls = $xml.eq(0).hasClass("thing");
// thing 클래스만 목록으로 반환.
// 이미 ID를 가지고 있으며, 클래스가 thing 인것만 목록으로 넘김.
if(typeof($elmid) != 'undefined' && $elmHasCls == true) {
els.push( { "id": $elmid, "xlocation": 100, "ylocation": 100, "size": 22 } );
}
});
* 구현: 키프레임 설명자를 이용한 회전 효과
CSS3은 키프레임 설명자(KeyFrame-Descriptor)를 지원하여 애니메이션의 키프레임의 변화를 지정할 수 있다. 가령, 키프레임이 변형된 후 원형으로 돌아오는데 걸리는 시간을 지정하여 애니메이션 효과를 지정할 수 있다. 예제는 아래와 같다.
.rotate {
-webkit-animation: rotation 2s infinite linear;
-moz-animation: rotation 2s infinite linear;
-o-animation: rotation 2s infinite linear;
animation: rotation 2s infinite linear;
transform-origin: 50% 50%;
-webkit-transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(359deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(359deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(359deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(359deg);}
}
위 예제는 회전이 중앙 축을 기준으로 0도에서 359도 (약 360도) 회전하는데 걸리는 시간(원형 키프레임으로 돌아오는 시간)을 지정하여 회전 애니메이션 효과를 지정한다. 역방향 키프레임 효과는 목적 각도(deg of to)에 마이너스(-) 값을 주어서 구현이 가능하다.
브라우저 벤더를 식별하는 문자로 webkit, moz, o 등이 존재하는데, 벤더 식별 문자가 들어가야만 해석을 하는 브라우저가 있기 때문에 같이 넣어주어야 한다.
* 구현: 프리셋을 이용한 조건에 따른 효과 변화
프리셋을 통해 조건에 따른 애니메이션이나 색상 효과를 지정할 수 있다. 패널 옵션에서 프리셋의 속성을 바꿔치기(override) 할 수 있도록 지원하여 사용자가 직접 지정할 수 있다.
{
"gt":{"value":100,"style":{"fill":"red"},"animate":{"spinspeed":9}},
"lt":{"value":1000,"style":{"fill":"yellow"},"animate":{"spinspeed":6}},
"gte":{"value":10000,"style":{"fill":"green"},"animate":{"spinspeed":3}},
"lte":{"value":100000,"style":{"fill":"white"},"animate":{"spinspeed":0}},
"eq":{"value":1000000,"style":{"fill":"gray"},"animate":{"spinspeed":12}},
"not":{"style":{"fill":"gray"}}
}
해당 속성을 바꿔치기(override)하는 것은 옵션 양식(editor.html)에서 입력을 지원하는 것으로도 가능하다. 또한 컨트롤러(ctrl.js)에서 override에 대한 더 상세한 조건을 코드로 지정할 수 있다. 아래는 override를 위해 사용자 값을 입력받는 양식의 예제이다.
Preset override
(... 생략 ...)
* 구현: 브라우저에서 표시되는 벡터 객체의 축좌표 계산
SVG 벡터 이미지의 좌표 속성은 사후결정의 특징이 있어 축 좌표를 지정하기 위해 사전에 중간 값을 뜻하는 50%를 수치로 지정해놓아도 정확한 축 좌표를 찾지 않는다.
즉 사후결정 방식에 맞게 객체 로드가 완료된 후 축 좌표를 계산하는 것이 필요하며 그 계산은 아래와 같이 가능하다.
// 중심 축좌표 계산
var coord = s.getBBox();
var cx = coord.x + (coord.width / 2);
var cy = coord.y + (coord.height / 2);
// 중심 축으로 회전
var $node = $(s.node);
$node.addClass("rotate");
$node.css({
"transform-origin": cx + "px " + cy + "px",
"-webkit-transform-origin": cx + "px " + cy + "px",
"-moz-transform-origin": cx + "px " + cy + "px"
});
* 구현: 다중 패널의 구현
랜덤 값 생성 함수를 이용하여 다중 패널을 구현할 수 있다. 플러그인의 객체 생성자(플러그인 클래스의 생성자)에서 랜덤 값을 이용하여 캔버스 이름(SVG가 표현될 바탕이 될 객체의 이름)을 지정한다. 모듈 템플릿은 생성된 캔버스 이름을 가지고 객체를 생성한다.
export class [blind] extends [blind] {
constructor($scope, $injector) {
super($scope, $injector);
_.defaults(this.panel, panelDefaults);
(... 생략 ....)
// 캔버스 코드 생성
this.panel.canvasname = "canvas_" + this.createRandomId('', 10);
}
이후 진행되는 과정은 앞서 명시된 SVG 벡터 객체 추적과 관련이 있으니 추가 개발 시 참고하면 된다.
* 적용: 컴파일 및 설치 방법
해당 플러그인의 컴파일(빌드) 명세는 grunt 빌드 환경에 의해 관리된다. 정상적인 빌드를 위해서는 각 프로젝트 내 디렉토리의 역할 및 작업을 지정할 필요가 있다. 아래는 빌드 시 실행될 작업(task)를 등록하는 예시이다.
(... 생략 ...)
assets: {
cwd: 'src',
expand: true,
src: ['**/assets/*'],
dest: 'dist'
},
extern: {
cwd: 'src',
expand: true,
src: ['**/extern/*'],
dest: 'dist'
}
}
(... 생략 ...)
babel: {
options: {
sourceMap: true,
presets: ['es2015'],
plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of'],
},
dist: {
files: [{
cwd: 'src',
expand: true,
src: ['*.js'],
dest: 'dist',
ext: '.js'
}]
},
},
(... 생략 ...)
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'copy:extern', 'copy:assets', 'babel']);
빌드는 프로젝트 디렉토리에서 ‘grunt’ 명령을 이용한다. 해당 명령을 이용하면 아래와 같은 결과를 얻을 수 있다. 이후 프로젝트 디렉토리 전체를 /var/lib/visualpanel/plugins 하위에 복사하면 설치는 완료된다. 아래 예제는 플러그인 디렉토리 내에서 빌드한 것이다.
root@compute:/var/lib/visualpanel/plugins/customspin-svgctrl-panel# grunt
Running "clean:0" (clean) task
>> 1 path cleaned.
(... 생략 ...)
Running "copy:extern" (copy) task
Created 1 directory, copied 9 files
Running "copy:assets" (copy) task
Created 2 directories, copied 219 files
Running "babel:dist" (babel) task
Done, without errors.
동적 대시보드 개발 명세문서 마침.