웹 앱은 Interactive Canvas를 사용하는 작업의 UI입니다. 기존 웹 기술 (HTML, CSS, 자바스크립트)을 사용하여 웹 앱을 디자인하고 개발할 수 있습니다. 대부분의 경우 Interactive Canvas는 브라우저와 같은 웹 콘텐츠를 렌더링할 수 있지만 사용자 개인 정보 보호 및 보안을 위해 몇 가지 제한사항이 적용됩니다. UI 디자인을 시작하기 전에 Design guidelines
섹션에 설명된 디자인 원칙을 고려하세요.
웹 앱의 HTML 및 JavaScript는 다음을 수행합니다.
- Interactive Canvas 이벤트 callbacks을 등록합니다.
- Interactive Canvas JavaScript 라이브러리를 초기화합니다.
- 상태에 따라 웹 앱을 업데이트하는 커스텀 로직을 제공합니다.
이 페이지에서는 웹 앱을 빌드하는 권장 방법, 웹 앱과 처리 간의 통신을 사용 설정하는 방법, 일반적인 가이드라인과 제한사항을 설명합니다.
추천 라이브러리
모든 메서드를 사용하여 UI를 빌드할 수 있지만 다음 라이브러리를 사용하는 것이 좋습니다.
- Greensock: 복잡한 애니메이션을 빌드하는 데 적합합니다.
- Pixi.js: WebGL에서 2D 그래픽을 그리는 데 사용됩니다.
- Three.js: WebGL에서 3D 그래픽을 그리는 데 사용됩니다.
- HTML5 캔버스 그리기: 간단한 그림에 적합합니다.
- DOM 요소: 정적 콘텐츠용입니다.
아키텍처
단일 페이지 애플리케이션 아키텍처를 사용하는 것이 좋습니다. 이 접근 방식을 사용하면 최적의 성능을 얻을 수 있으며 지속적인 대화 사용자 환경을 지원합니다. Interactive Canvas는 상태 관리에 도움이 되는 Vue, Angular, React 같은 프런트엔드 프레임워크와 함께 사용할 수 있습니다.
HTML 파일
HTML 파일은 UI의 모양을 정의합니다. 이 파일은 웹 앱과 대화형 작업 간의 커뮤니케이션을 지원하는 Interactive Canvas JavaScript 라이브러리도 로드합니다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Immersive Canvas Sample</title>
<!-- Disable favicon requests -->
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
<!-- Load Interactive Canvas JavaScript -->
<script src="https://www.gstatic.com/assistant/df-asdk/interactivecanvas/api/interactive_canvas.min.js"></script>
<!-- Load PixiJS for graphics rendering -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.7/pixi.min.js"></script>
<!-- Load Stats.js for fps monitoring -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<!-- Load custom CSS -->
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div id="view" class="view">
<div class="debug">
<div class="stats"></div>
<div class="logs"></div>
</div>
</div>
<!-- Load custom JavaScript after elements are on page -->
<script src="js/main.js"></script>
<script src="js/log.js"></script>
</body>
</html>
처리와 웹 앱 간 통신
웹 앱과 처리를 빌드하고 웹 앱 파일의 Interactive Canvas 라이브러리에 로드했으므로 이제 웹 앱과 처리가 상호작용하는 방식을 정의해야 합니다. 이렇게 하려면 웹 앱 로직이 포함된 파일을 수정합니다.
action.js
이 파일에는 callbacks을 정의하고 interactiveCanvas
를 통해 메서드를 호출하는 코드가 포함되어 있습니다. 콜백을 사용하면 웹 앱이 대화 작업에서 정보나 요청에 응답할 수 있고, 메서드는 대화 작업으로 정보나 요청을 보내는 방법을 제공합니다.
HTML 파일에 interactiveCanvas.ready(callbacks);
를 추가하여 callbacks을 초기화하고 등록합니다.
//action.js
class Action {
constructor(scene) {
this.canvas = window.interactiveCanvas;
this.scene = scene;
const that = this;
this.commands = {
TINT: function(data) {
that.scene.sprite.tint = data.tint;
},
SPIN: function(data) {
that.scene.sprite.spin = data.spin;
},
RESTART_GAME: function(data) {
that.scene.button.texture = that.scene.button.textureButton;
that.scene.sprite.spin = true;
that.scene.sprite.tint = 0x0000FF; // blue
that.scene.sprite.rotation = 0;
},
};
}
/**
* Register all callbacks used by Interactive Canvas
* executed during scene creation time.
*
*/
setCallbacks() {
const that = this;
// declare interactive canvas callbacks
const callbacks = {
onUpdate(data) {
try {
that.commands[data.command.toUpperCase()](data);
} catch (e) {
// do nothing, when no command is sent or found
}
},
};
// called by the Interactive Canvas web app once web app has loaded to
// register callbacks
this.canvas.ready(callbacks);
}
}
main.js
이 파일은 웹 앱의 장면을 구성합니다. 이 예에서는 sendTextQuery()
로 반환된 프로미스의 성공 사례와 실패 사례도 처리합니다. 다음은 main.js
에서 발췌한 것입니다.
// main.js
const view = document.getElementById('view');
// initialize rendering and set correct sizing
this.renderer = PIXI.autoDetectRenderer({
transparent: true,
antialias: true,
resolution: this.radio,
width: view.clientWidth,
height: view.clientHeight,
});
view.appendChild(this.element);
// center stage and normalize scaling for all resolutions
this.stage = new PIXI.Container();
this.stage.position.set(view.clientWidth / 2, view.clientHeight / 2);
this.stage.scale.set(Math.max(this.renderer.width,
this.renderer.height) / 1024);
// load a sprite from a svg file
this.sprite = PIXI.Sprite.from('triangle.svg');
this.sprite.anchor.set(0.5);
this.sprite.tint = 0x00FF00; // green
this.sprite.spin = true;
this.stage.addChild(this.sprite);
// toggle spin on touch events of the triangle
this.sprite.interactive = true;
this.sprite.buttonMode = true;
this.sprite.on('pointerdown', () => {
this.sprite.spin = !this.sprite.spin;
});
터치 상호작용 지원
Interactive Canvas 작업은 사용자의 터치와 음성 입력에 응답할 수 있습니다. Interactive Canvas 디자인 가이드라인에 따라 작업을 '음성 우선'으로 개발해야 합니다. 하지만 일부 스마트 디스플레이는 터치 상호작용을 지원합니다.
터치 지원은 대화형 응답을 지원하는 것과 유사합니다. 하지만 클라이언트 측 JavaScript는 사용자의 음성 응답 대신 터치 상호작용을 찾고 이를 사용하여 웹 앱의 요소를 변경합니다.
Pixi.js 라이브러리를 사용하는 샘플에서 이에 관한 예를 확인할 수 있습니다.
...
this.sprite = PIXI.Sprite.from('triangle.svg');
...
this.sprite.interactive = true; // Enables interaction events
this.sprite.buttonMode = true; // Changes `cursor` property to `pointer` for PointerEvent
this.sprite.on('pointerdown', () => {
this.sprite.spin = !this.sprite.spin;
});
...
이 경우 spin
변수의 값은 interactiveCanvas
API를 통해 update
콜백으로 전송됩니다. 처리에는 spin
값에 따라 인텐트를 트리거하는 로직이 있습니다.
...
app.intent('pause', (conv) => {
conv.ask(`Ok, I paused spinning. What else?`);
conv.ask(new HtmlResponse({
data: {
spin: false,
},
}));
});
...
다른 기능 추가
기본사항을 배웠으므로 이제 Canvas 관련 API를 사용하여 작업을 개선하고 맞춤설정할 수 있습니다. 이 섹션에서는 Interactive Canvas 작업에서 이러한 API를 구현하는 방법을 설명합니다.
sendTextQuery()
sendTextQuery()
메서드는 대화형 작업에 텍스트 쿼리를 전송하여 프로그래매틱 방식으로 인텐트를 호출합니다. 이 샘플은 sendTextQuery()
를 사용하여 사용자가 버튼을 클릭할 때 삼각형 회전 게임을 다시 시작합니다. 사용자가 '게임 다시 시작' 버튼을 클릭하면 sendTextQuery()
에서 Restart game
인텐트를 호출하고 프로미스를 반환합니다. 이 프로미스로 인해 인텐트가 트리거되면 SUCCESS
, 트리거되지 않으면 BLOCKED
이 발생합니다. 다음 스니펫은 인텐트를 트리거하고 프로미스의 성공 및 실패 사례를 처리합니다.
//main.js
...
that.action.canvas.sendTextQuery('Restart game')
.then((res) => {
if (res.toUpperCase() === 'SUCCESS') {
console.log(`Request in flight: ${res}`);
that.button.texture = that.button.textureButtonDisabled;
that.sprite.spin = false;
} else {
console.log(`Request in flight: ${res}`);
}
});
...
프로미스로 SUCCESS
가 반환되면 Restart game
인텐트가 웹 앱에 HtmlResponse
를 전송합니다.
//index.js
...
app.intent('restart game', (conv) => {
conv.ask(new HtmlResponse({
data: {
command: 'RESTART_GAME',
},
...
이 HtmlResponse
는 아래 RESTART_GAME
코드 스니펫의 코드를 실행하는 onUpdate()
콜백을 트리거합니다.
//action.js
...
RESTART_GAME: function(data) {
that.scene.button.texture = that.scene.button.textureButton;
that.scene.sprite.spin = true;
that.scene.sprite.tint = 0x0000FF; // blue
that.scene.sprite.rotation = 0;
},
...
OnTtsMark()
OnTtsMark()
콜백은 사용자에 대한 SSML 응답에 고유한 이름이 있는 <mark>
태그를 포함할 때 호출됩니다. Snowman 샘플의 다음 발췌 부분에서 OnTtsMark()
는 웹 앱의 애니메이션을 상응하는 TTS 출력과 동기화합니다. 작업이 사용자에게 죄송합니다. 분실하셨어요라고 말하면 웹 앱이 올바른 단어의 철자를 입력하고 사용자에게 문자를 표시합니다.
Game Over Reveal Word
인텐트는 게임에서 졌을 때 사용자에 대한 응답에 맞춤 마크를 포함합니다.
//index.js
...
app.intent('Game Over Reveal Word', (conv, {word}) => {
conv.ask(`<speak>Sorry, you lost.<mark name="REVEAL_WORD"/> The word is ${word}.` +
`${PLAY_AGAIN_INSTRUCTIONS}</speak>`);
conv.ask(new HtmlResponse());
});
...
그런 다음, 다음 코드 스니펫은 OnTtsMark()
콜백을 등록하고, 표시 이름을 확인하고, revealCorrectWord()
함수를 실행하여 웹 앱을 업데이트합니다.
//action.js
...
setCallbacks() {
const that = this;
// declare assistant canvas action callbacks
const callbacks = {
onTtsMark(markName) {
if (markName === 'REVEAL_WORD') {
// display the correct word to the user
that.revealCorrectWord();
}
},
...
제한사항
웹 앱을 개발할 때 다음 제한사항을 고려하세요.
- 쿠키 없음
- 로컬 저장소 없음
- 위치정보 없음
- 카메라 사용 안함
- 팝업 없음
- 메모리 한도 200MB 미만 유지
- 서드 파티 헤더가 화면 상단을 차지합니다.
- 동영상에 스타일을 적용할 수 없습니다.
- 한 번에 하나의 미디어 요소만 사용할 수 있습니다.
- HLS 동영상 없음
- 웹 SQL 데이터베이스 없음
- Web Speech API의
SpeechRecognition
인터페이스는 지원되지 않습니다. - 오디오 또는 동영상 녹화가 되지 않음
- 어두운 모드 설정을 적용할 수 없음
교차 출처 리소스 공유
Interactive Canvas 웹 앱은 iframe에서 호스팅되고 출처가 null로 설정되므로 웹 서버 및 스토리지 리소스에 교차 출처 리소스 공유 (CORS)를 사용 설정해야 합니다. 이렇게 하면 애셋이 null 출처의 요청을 수락할 수 있습니다.
- 미디어와 이미지가 Firebase에서 호스팅되는 경우 커스텀 도메인 동적 링크 만들기를 참조하여 CORS를 구성하세요.
- 미디어 및 이미지가 Cloud Storage에 있는 경우 교차 출처 리소스 공유 (CORS) 구성을 참조하여 CORS를 구성하세요.