Web 应用是使用 Interactive Canvas 的 Action 的界面。您可以使用
现有的网络技术(HTML、CSS 和 JavaScript)来设计和开发
您的 Web 应用。Interactive Canvas 在大多数情况下能够渲染 Web 内容,例如
但浏览器会强制实施一些限制
用户隐私和安全。在开始设计界面之前,请考虑
Design guidelines
中列出的设计原则
部分。
Web 应用的 HTML 和 JavaScript 会执行以下操作:
- 注册 Interactive Canvas 事件回调。
- 初始化 Interactive Canvas JavaScript 库。
- 提供根据状态更新 Web 应用的自定义逻辑。
本页面介绍了构建 Web 应用的推荐方法,以及如何启用 与执行方式之间的通信,以及一般准则和 限制。
推荐的库
尽管您可以使用任何方法来构建界面,但 Google 建议您使用以下 库:
- Greensock:用于构建复杂的动画。
- Pixi.js:用于在 WebGL 上绘制 2D 图形。
- Three.js:用于在 WebGL 上绘制 3D 图形。
- HTML5 画布绘图:适用于简单的绘图。
- DOM 元素:适用于静态内容。
架构
Google 强烈建议使用单页应用架构。 这种方法可实现最佳性能,并支持持续 对话式用户体验。Interactive Canvas 可以与 Vue 等前端框架, Angular 和 React, 帮助管理状态
HTML 文件
HTML 文件定义了界面的外观。此文件还会加载 Canvas JavaScript 库,可实现通信 在 Web 应用与对话型 Action 之间建立连接。
<!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>
在执行方式和 Web 应用之间通信
现在,您已经构建了自己的 Web 应用和执行方式,并在交互式 画布库 Web 应用文件,您需要定义 Web 应用和执行方式的交互方式。接收者 为此,请修改包含 Web 应用逻辑的文件。
action.js
此文件包含定义
回调
并调用方法
直到 interactiveCanvas
。回调可让你的 Web 应用
通过对话型 Action 提供信息或请求,而方法
提供一种向对话型 Action 发送信息或请求的方式。
将 interactiveCanvas.ready(callbacks);
添加到 HTML 文件中,以进行初始化
注册回调:
//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
此文件用于为您的 Web 应用构建场景。在此示例中,它还处理
通过 sendTextQuery()
返回的 promise 成功和失败情况。通过
以下是摘自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 Action 可以响应用户的轻触操作以及 其声音输入。根据 Interactive Canvas 设计指南 您应在开发 Action 时采用“语音优先”。尽管如此,一些 显示屏支持触摸互动。
支持触摸类似于支持对话式响应;不过, 而不是用户的语音响应,您的客户端 JavaScript 看起来 ,并使用此类互动来更改 Web 应用中的元素。
您可以在示例中看到示例,该示例使用 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
的值触发 intent)。
...
app.intent('pause', (conv) => {
conv.ask(`Ok, I paused spinning. What else?`);
conv.ask(new HtmlResponse({
data: {
spin: false,
},
}));
});
...
添加更多功能
至此你已经了解了基础知识,现在可以改进和自定义你的 Action 。本部分介绍了如何实现这些 API 。
sendTextQuery()
sendTextQuery()
方法向对话型 Action 发送文本查询
以编程方式调用 intent。此示例使用 sendTextQuery()
来
在用户点击按钮时重新开始旋转三角形游戏。当用户
请点击“重新开始游戏”按钮,sendTextQuery()
会调用 Restart game
intent 并返回 promise。如果 intent 是SUCCESS
如果未触发,则返回 BLOCKED
。以下代码段会触发 intent
并处理 promise 的成功和失败情况:
//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}`);
}
});
...
如果 promise 导致 SUCCESS
,Restart game
intent 会发送 HtmlResponse
添加到您的 Web 应用:
//index.js
...
app.intent('restart game', (conv) => {
conv.ask(new HtmlResponse({
data: {
command: 'RESTART_GAME',
},
...
此 HtmlResponse
会触发 onUpdate()
回调,该回调会执行代码
在下面的 RESTART_GAME
代码段中:
//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()
<mark>
SSML 响应。以下摘录自雪人示例,
OnTtsMark()
将 Web 应用的动画与相应 TTS 同步
输出。当该 Action 对用户说 Sorry, youlow 时,Web 应用会拼写出
猜出正确的单词并向用户显示字母。
intent 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()
函数,该函数会更新 Web 应用:
//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();
}
},
...
限制
在开发 Web 应用时,请考虑以下限制:
- 没有 cookie
- 没有本地存储空间
- 无地理定位
- 不使用摄像头
- 无弹出式窗口
- 不要超过 200 MB 的内存限制
- 第三方标题占据屏幕的上半部分
- 没有样式无法应用于视频
- 一次只能使用一个媒体元素
- 无 HLS 视频
- 无 Web SQL 数据库
- 不支持
SpeechRecognition
接口的 Web Speech API。 - 无法录制音频或视频
- 深色模式设置不适用
跨域资源共享
因为 Interactive Canvas Web 应用托管在 iframe 中,并且来源已设置 设置为 null,您必须启用跨域资源共享 (CORS) Web 服务器和存储资源所需的资源。这样,您的素材资源便可接受 来自 null 源的请求。
- 如果您的媒体和图片由 Firebase 托管,请参阅创建自定义 网域动态链接 配置 CORS
- 如果您的媒体和图片位于 Cloud Storage 上,请参阅配置跨源 资源共享 (CORS) 配置 CORS