1“'`–
목차
- 개요
- 업데이트 요약
- 구현: 스타일시트 동적 로딩
- 구현: SVG 벡터 객체의 추적 및 다루기
- 구현; 오브젝트 필터
- 구현: 키프레임 설명자를 이용한 회전 효과
- 구현: 프리셋을 이용한 조건에 따른 효과 변화
- 구현: 브라우저에서 표시되는 벡터 객체의 축좌표 계산
- 구현: 다중 패널 지원
- 적용: 컴파일 및 설치 방법
개요
본 문서는 모니터링 대시보드에서 SVG를 다루기 위한 추가 개발에 고려된 기술적 내용을 담았다.
업데이트 요약
- 오브젝트 필터 적용: 객체 클래스에 'thing'이 적용된 객체만 리스트로 보냄.
- 역방향 회전 적용: 패널 옵션에서 각 객체 항목의 'Reverse' 체크박스 선택.
- 다중 패널 지원: 다중 패널을 추가해도 정상적으로 동작하도록 개선.
- SVG 개발 문서 작성: 객체 축좌표 계산, 애니메이션 키프레임 설명자 등의 내용이 담김.
구현 상세
- 구현: 스타일시트 동적 로딩
CSS3에서 지원하는 스펙 사항을 플러그인에서 활용하기 위해 스타일시트(CSS)를 동적으로 요청할 필요성이 있다. jQuery를 이용하는 아래 구문으로 CSS의 동적 로드가 가능하다.
$("<link/>", { 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를 위해 사용자 값을 입력받는 양식의 예제이다.
<div class="section gf-form-group"> <h5 class="section-heading">Preset override</h5> <div class="gf-form"> <label class="gf-form-label width-4">std</label> <input type="text" class="input-small gf-form-input width-8" ng-model="sensor.std" ng-change="ctrl.render()" ng-model-onblur /> </div> (... 생략 ...) </div>
- 구현: 브라우저에서 표시되는 벡터 객체의 축좌표 계산
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.
동적 대시보드 개발 명세문서 마침.