===== 목차 ===== * 개요 * 업데이트 요약 * 구현: 스타일시트 동적 로딩 * 구현: 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. 동적 대시보드 개발 명세문서 마침.