สร้างเว็บแอป (Dialogflow)

เว็บแอปคือ 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) สำหรับเว็บเซิร์ฟเวอร์และทรัพยากรพื้นที่เก็บข้อมูล ซึ่งจะช่วยให้เนื้อหายอมรับคำขอจากต้นทางที่ไม่มีข้อมูล