เว็บแอปคือ UI สำหรับการดำเนินการที่ใช้ Canvas แบบอินเทอร์แอกทีฟ คุณสามารถใช้เทคโนโลยีเว็บที่มีอยู่ (HTML, CSS และ JavaScript) เพื่อออกแบบและพัฒนาเว็บแอป โดยส่วนใหญ่แล้ว Interactive Canvas จะแสดงผลเนื้อหาเว็บอย่างเช่นเบราว์เซอร์ได้ แต่มีข้อจำกัดบางประการที่บังคับใช้กับความเป็นส่วนตัวและความปลอดภัยของผู้ใช้ ก่อนที่จะเริ่มออกแบบ UI ให้ลองพิจารณาหลักการออกแบบที่ระบุไว้ในส่วน Design guidelines
HTML และ JavaScript สำหรับเว็บแอปมีหน้าที่ดังต่อไปนี้
- ลงทะเบียนcallbacksของเหตุการณ์ Interactive Canvas
- เริ่มต้นไลบรารี JavaScript ของ Interactive Canvas
- ระบุตรรกะที่กำหนดเองสำหรับการอัปเดตเว็บแอปตามสถานะ
หน้านี้จะกล่าวถึงวิธีที่แนะนำในการสร้างเว็บแอป วิธีเปิดใช้การสื่อสารระหว่างเว็บแอปกับ Fulfillment ตลอดจนหลักเกณฑ์ทั่วไปและข้อจำกัด
ไลบรารีที่แนะนำ
แม้ว่าคุณจะสร้าง UI โดยใช้วิธีใดก็ได้ แต่ Google ขอแนะนำให้ใช้ไลบรารีต่อไปนี้
- Greensock: สำหรับการสร้างภาพเคลื่อนไหวที่ซับซ้อน
- Pixi.js: สำหรับการวาดกราฟิก 2 มิติบน WebGL
- Three.js: สำหรับการวาดกราฟิก 3 มิติบน WebGL
- ภาพวาด Canvas แบบ HTML5: สำหรับภาพวาดง่ายๆ
- องค์ประกอบ DOM: สำหรับเนื้อหาแบบคงที่
สถาปัตยกรรม
Google ขอแนะนำให้ใช้สถาปัตยกรรมแอปพลิเคชันหน้าเว็บเดียว วิธีนี้ทำให้เกิดประสิทธิภาพสูงสุดและรองรับประสบการณ์ของผู้ใช้ในการสนทนาอย่างต่อเนื่อง Interactive Canvas สามารถใช้ร่วมกับเฟรมเวิร์กฟรอนท์เอนด์ได้ เช่น Vue, Angular และ React ซึ่งจะช่วยในการจัดการสถานะได้
ไฟล์ HTML
ไฟล์ HTML จะกำหนดหน้าตาของ UI ไฟล์นี้ยังโหลดไลบรารี 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>
สื่อสารระหว่าง Fulfillment กับเว็บแอป
เมื่อสร้างเว็บแอปและ Fulfillment และโหลดในไลบรารี Canvas แบบอินเทอร์แอกทีฟในไฟล์เว็บแอปแล้ว คุณต้องกำหนดการทำงานร่วมกันระหว่างเว็บแอปและ Fulfillment โดยให้แก้ไขไฟล์ที่มีตรรกะเว็บแอป
action.js
ไฟล์นี้มีโค้ดสำหรับกำหนด callbacks และเรียกใช้ methods ผ่าน interactiveCanvas
โค้ดเรียกกลับช่วยให้เว็บแอปตอบกลับข้อมูลหรือคำขอจากการดำเนินการแบบบทสนทนาได้ ในขณะที่เมธอด
ช่วยให้ส่งข้อมูลหรือคำขอไปยังการดำเนินการแบบบทสนทนา
เพิ่ม interactiveCanvas.ready(callbacks);
ลงในไฟล์ HTML เพื่อเริ่มต้นและลงทะเบียน 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;
});
รองรับการโต้ตอบการสัมผัส
การดำเนินการ 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
จะส่งผ่าน API interactiveCanvas
เป็นโค้ดเรียกกลับ update
Fulfillment มีตรรกะที่ทริกเกอร์ Intent ตามค่าของ spin
...
app.intent('pause', (conv) => {
conv.ask(`Ok, I paused spinning. What else?`);
conv.ask(new HtmlResponse({
data: {
spin: false,
},
}));
});
...
เพิ่มฟีเจอร์
เมื่อได้เรียนรู้พื้นฐานแล้ว คุณสามารถเพิ่มประสิทธิภาพและปรับแต่งการทำงานด้วย API สำหรับ Canvas โดยเฉพาะได้ ส่วนนี้จะอธิบายวิธีใช้ API เหล่านี้ใน Canvas แบบอินเทอร์แอกทีฟ
sendTextQuery()
เมธอด sendTextQuery()
จะส่งการค้นหาข้อความไปยังการดำเนินการแบบบทสนทนาเพื่อเรียกใช้ Intent แบบเป็นโปรแกรม ตัวอย่างนี้ใช้ sendTextQuery()
เพื่อเริ่มเกมหมุนรูปสามเหลี่ยมอีกครั้งเมื่อผู้ใช้คลิกปุ่ม เมื่อผู้ใช้คลิกปุ่ม "เริ่มเกมใหม่" sendTextQuery()
จะเรียก Intent Restart game
และแสดงผลสัญญา คำสัญญานี้จะแสดงผลใน SUCCESS
หาก Intent ถูกเรียกใช้ และ 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
Intent ของ Restart game
จะส่ง HtmlResponse
ไปยังเว็บแอปของคุณดังนี้
//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 ไปยังผู้ใช้ ในข้อความที่ตัดตอนมาจากตัวอย่าง Snowman ต่อไปนี้
OnTtsMark()
จะซิงค์ภาพเคลื่อนไหวของเว็บแอปกับเอาต์พุต TTS ที่เกี่ยวข้อง เมื่อการดำเนินการพูดกับผู้ใช้ว่าขออภัย คุณทำหาย เว็บแอปจะสะกดคำที่ถูกต้องและแสดงตัวอักษรให้ผู้ใช้
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()
ซึ่งจะอัปเดตเว็บแอป
//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();
}
},
...
ข้อจำกัด
โปรดพิจารณาข้อจำกัดต่อไปนี้ขณะที่คุณพัฒนาเว็บแอป
- ไม่มีคุกกี้
- ไม่มีพื้นที่เก็บข้อมูลในเครื่อง
- ไม่มีตำแหน่งทางภูมิศาสตร์
- ไม่มีการใช้งานกล้อง
- ไม่มีป๊อปอัป
- มีขนาดไม่เกิน 200 MB
- ส่วนหัวของบุคคลที่สามใช้พื้นที่ส่วนบนของหน้าจอ
- ไม่มีสไตล์ที่นำมาใช้กับวิดีโอได้
- ใช้องค์ประกอบสื่อได้ครั้งละ 1 รายการเท่านั้น
- ไม่มีวิดีโอ HLS
- ไม่มีฐานข้อมูล Web SQL
- ไม่รองรับอินเทอร์เฟซ
SpeechRecognition
ของ Web Speech API - ไม่มีการบันทึกเสียงหรือวิดีโอ
- การตั้งค่าโหมดมืดไม่สามารถใช้ได้
การแชร์ทรัพยากรข้ามโดเมน
เนื่องจากเว็บแอป Interactive Canvas โฮสต์อยู่ใน iframe และตั้งค่าต้นทางเป็น Null คุณจึงต้องเปิดใช้การแชร์ทรัพยากรแบบข้ามต้นทาง (CORS) สำหรับเว็บเซิร์ฟเวอร์และทรัพยากรพื้นที่เก็บข้อมูล ซึ่งจะช่วยให้เนื้อหายอมรับคำขอจากต้นทางที่ไม่มีข้อมูล
- หากสื่อและรูปภาพโฮสต์อยู่กับ Firebase ให้ดูสร้างลิงก์แบบไดนามิกของโดเมนที่กำหนดเองเพื่อกำหนดค่า CORS
- หากสื่อและรูปภาพอยู่ใน Cloud Storage โปรดดูการกำหนดค่าการแชร์ทรัพยากรแบบข้ามต้นทาง (CORS) เพื่อกำหนดค่า CORS