Angular 2のRenderer
おのうえ (@_likr)
ng-kyoto Angular Meetup #4
自己紹介
• おのうえ (@_likr)
• 京都大学 政策のための科学ユニット 特定助教 🆕
• ng-kyoto オーガナイザー

GDG神戸 スタッフ
• 可視化アプリケーションの

Webベース実装
Renderer ?
Renderer
• https://angular.io/docs/ts/latest/api/core/Renderer-class.html
• WHAT IT DOES
• Not yet documented
• HOW TO USE
• Not yet documented
Not yet documented
はじめに
• Angular 2のRendererで遊んでみる
• 明日使えなくなっても泣かない
Rendererの概要
• RootRenderer
• Rendererを返す
• Renderer
• レンダリングする
• DomRenderer (default)
• WorkerRenderer
• DebugDomRenderer
• RootRendererをDIする
RendererのInterface
• selectRootElement
• createElement
• createViewRoot
• createTemplateAnchor
• createText
• projectNodes
• attachViewAfter
• detachView
• destroyView
• listen
• listenGlobal
• setElementProperty
• setElementAttribute
• setBindingDebugInfo
• setElementClass
• setElementStyle
• invokeElementMethod
• setText
https://angular.io/docs/ts/latest/api/core/Renderer-class.html
簡単なRendererを作る
import {Injectable} from 'angular2/core';
import {
Renderer,
RootRenderer,
RenderComponentType,
RenderDebugInfo
} from 'angular2/src/core/render/api';
@Injectable()
export class MyRootRenderer implements RootRenderer {
renderComponent(componentProto: RenderComponentType): Renderer {
return new MyRenderer();
}
}
class MyRenderer implements Renderer {
selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo) : any {
console.log('selectRootElement', selectorOrNode, debugInfo);
return {};
}
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo) : any {
console.log('createElement', parentElement, name, debugInfo);
return {};
}
// …
}
my-renderer.ts
自作Rendererを使う
import {Component} from 'angular2/core';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
import {provide, RootRenderer} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
import {MyRootRenderer} from './my-renderer'
bootstrap(AppComponent, [
provide(RootRenderer, { useClass: MyRootRenderer }),
]);
app.component.ts
main.ts
動かす
https://plnkr.co/edit/ifMaZknans8yAAA8sSAV
何かできないかな?
もしかして
• Angularで3Dグラフィックスができるのでは!?
• Scene Graph
• 宣言的に3Dオブジェクトを作成
• Change Detectionの恩恵
• イベントハンドリングの抽象化
THREE.jsベースのRenderer
import {Injectable} from 'angular2/core';
import {
Renderer,
RootRenderer,
RenderComponentType,
RenderDebugInfo
} from 'angular2/src/core/render/api';
@Injectable()
export class MyRootRenderer implements RootRenderer {
scene: THREE.Scene; camera: THREE.Camera; renderer: THREE.WebGLRenderer;
constructor() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
this.camera.position.z = 5;
this.renderer = new THREE.WebGLRenderer();
}
renderComponent(componentProto: RenderComponentType): Renderer {
return new MyRenderer(this);
}
}
THREE.jsベースのRenderer
class MyRenderer implements Renderer {
constructor(private rootRenderer: MyRootRenderer) {
}
selectRootElement(selector: string, debugInfo: RenderDebugInfo) : any {
var element = document.querySelector(selector);
this.rootRenderer.renderer.setSize(600, 600);
element.innerHTML = ''; element.appendChild(this.rootRenderer.renderer.domElement);
var render = () => {
requestAnimationFrame(render);
this.rootRenderer.renderer.render(this.rootRenderer.scene, this.rootRenderer.camera);
};
render();
return this.rootRenderer.scene;
}
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo) : any {
switch (name) {
case 'three-box':
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial();
var cube = new THREE.Mesh(geometry, material);
this.rootRenderer.scene.add(cube);
return cube;
// …
default:
throw 'unknown element';
}
}
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) : void {
switch (attributeName) {
case 'color':
renderElement.material.color = new THREE.Color(attributeValue);
break;
// …
}
}
}
描画してみる
https://plnkr.co/edit/yB0J8nVBK0KUiwkFO1VC
import {Component} from "angular2/core"
@Component({
selector: 'my-app',
template: '<three-box></three-box>',
})
export class AppComponent {
}
描画してみる
https://plnkr.co/edit/yB0J8nVBK0KUiwkFO1VC
Data Bindingできる
https://plnkr.co/edit/H4D1gmksKC0LH5AKHA3i
import {Component} from "angular2/core"
@Component({
selector: 'my-app',
template: `
<three-box
[attr.color]="boxColor">
</three-box>
`,
})
export class AppComponent {
boxColor = 'rgb(0, 255, 0)';
}
Data Bindingできる
https://plnkr.co/edit/H4D1gmksKC0LH5AKHA3i
import {Component, OnInit} from "angular2/core"
@Component({
selector: 'my-app',
template: `
<three-box [attr.color]="boxColor"
[attr.rotationX]="rotationX" [attr.rotationY]="rotationY">
</three-box>
`,
})
export class AppComponent implements OnInit {
boxColor = 'rgb(0, 255, 0)';
rotationX = 0; rotationY = 0;
ngOnInit() {
this.updateRotation();
}
updateRotation() {
this.rotationX += 0.1; this.rotationY += 0.1;
requestAnimationFrame(() => this.updateRotation());
}
}
ChangeDetectionが効く
https://plnkr.co/edit/EbqstFqCrsyVkZqLSWfY
ChangeDetectionが効く
https://plnkr.co/edit/EbqstFqCrsyVkZqLSWfY
ngForも使える
https://plnkr.co/edit/SAxskSugUVmBWFFcfiMC
import {Component, OnInit} from "angular2/core"
@Component({
selector: 'my-app',
template: `
<three-box *ngFor="let box of boxes"
[attr.color]="box.color" [attr.positionX]="box.x"
[attr.rotationX]="rotationX" [attr.rotationY]="rotationY">
</three-box>
`,
})
export class AppComponent implements OnInit {
boxes = [
{color: 'rgb(255, 0, 0)', x: -3},
{color: 'rgb(0, 255, 0)', x: 0},
{color: 'rgb(0, 0, 255)', x: 3},
];
rotationX = 0; rotationY = 0;
ngOnInit() {
this.updateRotation();
}
updateRotation() {
this.rotationX += 0.1; this.rotationY += 0.1;
requestAnimationFrame(() => this.updateRotation());
}
}
ngForも使える
https://plnkr.co/edit/SAxskSugUVmBWFFcfiMC
import {Component, OnInit} from "angular2/core"
var rotateColor = (box) => {
switch (box.color) {
case 'rgb(255, 0, 0)': box.color = 'rgb(0, 255, 0)'; break;
case 'rgb(0, 255, 0)': box.color = 'rgb(0, 0, 255)'; break;
case 'rgb(0, 0, 255)': default: box.color = 'rgb(255, 0, 0)';
}
};
@Component({
selector: 'my-app',
template: `
<three-box *ngFor="let box of boxes"
[attr.color]="box.color" [attr.positionX]="box.x"
[attr.rotationX]="rotationX" [attr.rotationY]="rotationY"
(click)="handleClick(box)">
</three-box>
`,
})
export class AppComponent implements OnInit{
boxes = [
{color: 'rgb(255, 0, 0)', x: -3}, {color: 'rgb(0, 255, 0)', x: 0}, {color: 'rgb(0, 0, 255)', x: 3},
];
rotationX = 0; rotationY = 0;
ngOnInit() {
this.updateRotation();
}
updateRotation() {
this.rotationX += 0.1; this.rotationY += 0.1;
requestAnimationFrame(() => this.updateRotation());
}
handleClick(box) {
rotateColor(box);
}
}
EventHandlingもできる
https://plnkr.co/edit/uy6oUMVoUc1DQ0YSvG8V
嬉しい!
でも、
こんなことやる
APIじゃないと思う
まとめ
• Renderer APIでHTMLのレンダリングをカスタマイズ
• One Framework
• Angular 2は、Component指向で構築された

XMLっぽい文書を解釈して

UIっぽい何かを開発するフレームワーク!?
• 多分違う
• ご利用は計画的に
Discussion

Angular 2のRenderer

  • 1.
  • 2.
    自己紹介 • おのうえ (@_likr) •京都大学 政策のための科学ユニット 特定助教 🆕 • ng-kyoto オーガナイザー
 GDG神戸 スタッフ • 可視化アプリケーションの
 Webベース実装
  • 3.
  • 4.
    Renderer • https://angular.io/docs/ts/latest/api/core/Renderer-class.html • WHATIT DOES • Not yet documented • HOW TO USE • Not yet documented
  • 5.
  • 6.
    はじめに • Angular 2のRendererで遊んでみる •明日使えなくなっても泣かない
  • 7.
    Rendererの概要 • RootRenderer • Rendererを返す •Renderer • レンダリングする • DomRenderer (default) • WorkerRenderer • DebugDomRenderer • RootRendererをDIする
  • 8.
    RendererのInterface • selectRootElement • createElement •createViewRoot • createTemplateAnchor • createText • projectNodes • attachViewAfter • detachView • destroyView • listen • listenGlobal • setElementProperty • setElementAttribute • setBindingDebugInfo • setElementClass • setElementStyle • invokeElementMethod • setText https://angular.io/docs/ts/latest/api/core/Renderer-class.html
  • 9.
    簡単なRendererを作る import {Injectable} from'angular2/core'; import { Renderer, RootRenderer, RenderComponentType, RenderDebugInfo } from 'angular2/src/core/render/api'; @Injectable() export class MyRootRenderer implements RootRenderer { renderComponent(componentProto: RenderComponentType): Renderer { return new MyRenderer(); } } class MyRenderer implements Renderer { selectRootElement(selectorOrNode: string | any, debugInfo: RenderDebugInfo) : any { console.log('selectRootElement', selectorOrNode, debugInfo); return {}; } createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo) : any { console.log('createElement', parentElement, name, debugInfo); return {}; } // … } my-renderer.ts
  • 10.
    自作Rendererを使う import {Component} from'angular2/core'; @Component({ selector: 'my-app', template: '<h1>My First Angular 2 App</h1>' }) export class AppComponent { } import {provide, RootRenderer} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {AppComponent} from './app.component'; import {MyRootRenderer} from './my-renderer' bootstrap(AppComponent, [ provide(RootRenderer, { useClass: MyRootRenderer }), ]); app.component.ts main.ts
  • 11.
  • 12.
  • 13.
    もしかして • Angularで3Dグラフィックスができるのでは!? • SceneGraph • 宣言的に3Dオブジェクトを作成 • Change Detectionの恩恵 • イベントハンドリングの抽象化
  • 14.
    THREE.jsベースのRenderer import {Injectable} from'angular2/core'; import { Renderer, RootRenderer, RenderComponentType, RenderDebugInfo } from 'angular2/src/core/render/api'; @Injectable() export class MyRootRenderer implements RootRenderer { scene: THREE.Scene; camera: THREE.Camera; renderer: THREE.WebGLRenderer; constructor() { this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000); this.camera.position.z = 5; this.renderer = new THREE.WebGLRenderer(); } renderComponent(componentProto: RenderComponentType): Renderer { return new MyRenderer(this); } }
  • 15.
    THREE.jsベースのRenderer class MyRenderer implementsRenderer { constructor(private rootRenderer: MyRootRenderer) { } selectRootElement(selector: string, debugInfo: RenderDebugInfo) : any { var element = document.querySelector(selector); this.rootRenderer.renderer.setSize(600, 600); element.innerHTML = ''; element.appendChild(this.rootRenderer.renderer.domElement); var render = () => { requestAnimationFrame(render); this.rootRenderer.renderer.render(this.rootRenderer.scene, this.rootRenderer.camera); }; render(); return this.rootRenderer.scene; } createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo) : any { switch (name) { case 'three-box': var geometry = new THREE.BoxGeometry(1, 1, 1); var material = new THREE.MeshBasicMaterial(); var cube = new THREE.Mesh(geometry, material); this.rootRenderer.scene.add(cube); return cube; // … default: throw 'unknown element'; } } setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) : void { switch (attributeName) { case 'color': renderElement.material.color = new THREE.Color(attributeValue); break; // … } } }
  • 16.
    描画してみる https://plnkr.co/edit/yB0J8nVBK0KUiwkFO1VC import {Component} from"angular2/core" @Component({ selector: 'my-app', template: '<three-box></three-box>', }) export class AppComponent { }
  • 17.
  • 18.
    Data Bindingできる https://plnkr.co/edit/H4D1gmksKC0LH5AKHA3i import {Component}from "angular2/core" @Component({ selector: 'my-app', template: ` <three-box [attr.color]="boxColor"> </three-box> `, }) export class AppComponent { boxColor = 'rgb(0, 255, 0)'; }
  • 19.
  • 20.
    import {Component, OnInit}from "angular2/core" @Component({ selector: 'my-app', template: ` <three-box [attr.color]="boxColor" [attr.rotationX]="rotationX" [attr.rotationY]="rotationY"> </three-box> `, }) export class AppComponent implements OnInit { boxColor = 'rgb(0, 255, 0)'; rotationX = 0; rotationY = 0; ngOnInit() { this.updateRotation(); } updateRotation() { this.rotationX += 0.1; this.rotationY += 0.1; requestAnimationFrame(() => this.updateRotation()); } } ChangeDetectionが効く https://plnkr.co/edit/EbqstFqCrsyVkZqLSWfY
  • 21.
  • 22.
    ngForも使える https://plnkr.co/edit/SAxskSugUVmBWFFcfiMC import {Component, OnInit}from "angular2/core" @Component({ selector: 'my-app', template: ` <three-box *ngFor="let box of boxes" [attr.color]="box.color" [attr.positionX]="box.x" [attr.rotationX]="rotationX" [attr.rotationY]="rotationY"> </three-box> `, }) export class AppComponent implements OnInit { boxes = [ {color: 'rgb(255, 0, 0)', x: -3}, {color: 'rgb(0, 255, 0)', x: 0}, {color: 'rgb(0, 0, 255)', x: 3}, ]; rotationX = 0; rotationY = 0; ngOnInit() { this.updateRotation(); } updateRotation() { this.rotationX += 0.1; this.rotationY += 0.1; requestAnimationFrame(() => this.updateRotation()); } }
  • 23.
  • 24.
    import {Component, OnInit}from "angular2/core" var rotateColor = (box) => { switch (box.color) { case 'rgb(255, 0, 0)': box.color = 'rgb(0, 255, 0)'; break; case 'rgb(0, 255, 0)': box.color = 'rgb(0, 0, 255)'; break; case 'rgb(0, 0, 255)': default: box.color = 'rgb(255, 0, 0)'; } }; @Component({ selector: 'my-app', template: ` <three-box *ngFor="let box of boxes" [attr.color]="box.color" [attr.positionX]="box.x" [attr.rotationX]="rotationX" [attr.rotationY]="rotationY" (click)="handleClick(box)"> </three-box> `, }) export class AppComponent implements OnInit{ boxes = [ {color: 'rgb(255, 0, 0)', x: -3}, {color: 'rgb(0, 255, 0)', x: 0}, {color: 'rgb(0, 0, 255)', x: 3}, ]; rotationX = 0; rotationY = 0; ngOnInit() { this.updateRotation(); } updateRotation() { this.rotationX += 0.1; this.rotationY += 0.1; requestAnimationFrame(() => this.updateRotation()); } handleClick(box) { rotateColor(box); } } EventHandlingもできる https://plnkr.co/edit/uy6oUMVoUc1DQ0YSvG8V
  • 25.
  • 26.
  • 27.
  • 28.
    まとめ • Renderer APIでHTMLのレンダリングをカスタマイズ •One Framework • Angular 2は、Component指向で構築された
 XMLっぽい文書を解釈して
 UIっぽい何かを開発するフレームワーク!? • 多分違う • ご利用は計画的に
  • 29.