Chrome 开发 - 第二部分:应用程序






4.78/5 (5投票s)
本文介绍如何编写在 Chrome 浏览器中运行的应用。
1. 引言
上一篇文章讨论了 Chrome 扩展程序,而本文则侧重于 Chrome 应用的开发。这两者有很多共同之处,它们之间的区别乍一看可能显得很微妙。为了帮助理解,先来区分三个术语:
- 软件应用程序 —— 用于帮助用户执行某项活动的软件,例如处理文本、编辑图像或玩游戏。通常具有专用用户界面。
- Web 应用 —— 在浏览器中运行的软件应用程序(应用程序中的应用程序)。
- 浏览器扩展程序 —— 用于自定义浏览体验或增强浏览器功能的软件。很少有用户界面。
Chrome 应用(通常简称 应用)是在 Chrome 浏览器中运行的 Web 应用。对用户而言,它的外观和感觉与普通 Web 应用无异,但它可以访问普通应用程序无法触及的丰富功能。缺点是 Chrome 应用只能在 Chrome 中运行。
从开发者的角度来看,Chrome 应用和扩展程序之间的区别很明显。在扩展程序中,内容脚本提供对 Web 文档的访问,弹出窗口提供基本的用户界面。应用没有内容脚本或弹出窗口,但用户界面要强大得多——应用可以在独立于浏览器之外的窗口中运行。
除了用户界面,应用相对于扩展程序的另一个优势在于对用户系统的底层访问。Chrome 应用可以访问硬件功能,包括蓝牙、TCP/UDP 套接字和通用串行总线 (USB)。它们还可以读取和写入用户的文件系统。我个人最喜欢的功能是 tts(文本转语音),它允许应用生成多种不同语言的语音。
本文将介绍如何编写创建窗口、访问用户文件系统以及将文本转换为语音的应用。但在深入研究代码之前,理解 Chrome 应用的结构以及它在浏览器中启动的方式非常重要。这些主题将在下一节中讨论。
2. 应用开发基础
Chrome 应用的文件结构几乎与扩展程序相同。需要注意三个基本特征:
- 应用的功能和能力必须在名为 manifest.json 的文件中定义。
- 应用可以有后台脚本,但不能有内容脚本或弹出窗口。
- 与扩展程序一样,应用可以通过 Chrome 开发者仪表板进行管理,网址为 chrome://extensions。
本节将首先讨论 manifest.json 的格式,然后介绍安装和启动一个简单应用的流程。
2.1 manifest.json
应用的 manifest 接受许多与扩展程序 manifest 相同的属性。这些属性包括 manifest_version、name、description、version、icons 和 permissions。主要区别在于应用的 manifest 必须有一个名为 app 的属性。该属性包含一个 background 属性,该属性将 scripts 字段与包含一个或多个后台脚本的数组关联起来。
如果下载了本文的示例存档,您会发现一个名为 basic_app 的文件夹。此应用的 manifest.json 易于理解:
{
  "manifest_version": 2,
  "name": "Trivial app",
  "description": "This app doesn't do anything",
  "version": "1.0",
  "icons": { "16": "images/cog_sm.png",
             "48": "images/cog_med.png",   
             "128": "images/cog_lg.png" },  
  "app": {
    "background": {
      "scripts": ["scripts/background.js"]
    }
  }
}
这应该看起来很熟悉,因为大多数属性的名称和值与上一篇文章中介绍的扩展程序相同。请记住,scripts 数组必须至少命名一个后台脚本。本文的其余部分将讨论应用后台脚本可以访问的许多功能。
2.2 加载和启动应用
如果您打开 Chrome 并访问 chrome://apps,您会看到所有可执行的应用。但该页面不允许您安装新应用。要管理应用,您需要访问上一篇文章中讨论的 Chrome 开发者仪表板。网址是 chrome://extensions,如果勾选了 **开发者模式** 框,页面顶部将显示如图 1 所示的内容。
图 1:开发者模式下的 Chrome 开发者仪表板

左侧的 **加载已解压的扩展程序...** 按钮可以允许您选择一个文件夹并将应用加载到 Chrome 中。理解此过程的最佳方法是进行一次示例演示。
本文附带的 example_code.zip 存档包含一个名为 basic_app 的文件夹。要将此应用加载到 Chrome 中,请按照以下四个步骤操作:
- 下载 example_code.zip 并解压存档。
- 在 Chrome 中,通过导航到 chrome://extensions 打开 Chrome 开发者仪表板。
- 按 **加载已解压的扩展程序...** 按钮并选择 basic_app 文件夹。
- 按确定加载应用。
当扩展程序加载后,将在仪表板的扩展程序列表中添加一个条目。图 2 显示了它的样子。
图 2:Chrome 开发者仪表板中的新条目

注意到 **启动** 链接很重要。与扩展程序不同,应用在安装时不会开始执行。应用必须被显式启动。
**重新加载** 链接会将应用重新加载到 Chrome 中。底部 **后台页面** 链接会打开应用的后台页面。这在您想查看与后台脚本关联的控制台时很有用。
3. chrome.app.runtime 和 chrome.app.window
应用和扩展程序的主要区别在于,应用可以在自己的窗口中执行。要创建这些窗口,应用必须调用两个 API 的函数:chrome.app.runtime 和 chrome.app.window。第一个 API 使应用能够响应其生命周期中的事件。第二个 API 提供了创建和配置窗口的函数。
3.1 chrome.app.runtime
chrome.app.runtime API(不要与 chrome.runtime API 混淆)定义了三个事件。表 1 列出了每个事件。
表 1:chrome.app.runtime 的事件
| 事件 | 触发器 | 
|---|---|
| onLaunched | 当应用执行时触发 | 
| onRestarted | 当应用重启时触发 | 
| onEmbedRequested | 当另一个应用尝试嵌入该应用时触发 | 
每个事件都有一个名为 addListener 的函数。它接受一个回调函数,在事件触发时调用。例如,以下代码定义了一个匿名函数,在应用启动时调用:
chrome.app.runtime.onLaunched.addListener(function(...) {
  ...
});
事件回调函数接收的参数取决于事件类型。
3.2 chrome.app.window
要定义其用户界面,应用可以创建一个或多个窗口。在代码中,应用的窗口由 AppWindow 对象表示。chrome.app.window API 的函数使创建和管理 AppWindow 成为可能,它们列在表 2 中。
表 2:chrome.app.window 的函数
| 函数 | 描述 | 
|---|---|
| create(string url,  CreateWindowOptions opts,  function callback) | 创建一个新的 AppWindow | 
| current() | 返回当前 AppWindow | 
| get(string id) | 返回具有给定名称的 AppWindow | 
| getAll() | 返回包含所有 AppWindow的数组 | 
| canSetVisibleOnAllWorkspace() | 标识平台是否支持在所有工作区上显示 窗口 | 
create 是需要了解的主要函数。它唯一的必需参数是第一个,它标识定义窗口结构和外观的 HTML 文件。第三个参数是一个回调函数,该函数接收新创建的 AppWindow。
create 的第二个参数是一个 CreateWindowOptions 对象。它有五个可选属性:
- id— 唯一标识窗口的字符串(与应用的 ID 无关)
- outerBounds— 一个- BoundsSpecification,用于设置窗口的位置和大小
- innerBounds— 一个- BoundsSpecification,用于设置窗口内容的位置和大小
- frame— 框架类型:- none或- chrome(默认为- chrome)
- state— 窗口的初始状态:- normal、- fullscreen、- maximized或- minimized。
第二个和第三个属性是 BoundsSpecification。BoundsSpecification 有八个属性用于定义位置和大小:left、top、width、height、minWidth、minHeight、maxWidth 和 maxHeight。例如,以下代码创建一个 200x200 的 AppWindow,该窗口的最小尺寸为 100x100 像素,最大尺寸为 300x300 像素:
chrome.app.window.create("window.html", 
  { "outerBounds": {"width": 200, "height": 200, 
                    "minWidth": 100, "minHeight": 100, 
                    "maxWidth": 300, "maxHeight": 300}},
  function(win) {
    ...
  }
);
如代码所示,create 的第三个参数是一个回调函数,它在 AppWindow 创建后接收它。表 3 列出了 AppWindow 对象的一些属性。
表 3:AppWindow 属性
| 属性 | 描述 | 
|---|---|
| id | 窗口的 ID,如果可用 | 
| outerBounds | 窗口的边界 | 
| innerBounds | 窗口内部内容的边界 | 
| contentWindow | 相应的 JavaScript window对象 | 
| fullscreen() | 将窗口调整为充满屏幕 | 
| isFullscreen() | 标识窗口是否占据屏幕 | 
| minimize() | 最小化窗口大小 | 
| isMinimized() | 标识窗口是否已最小化 | 
| maximize() | 最大化窗口大小 | 
| restore() | 将窗口恢复到其原始大小/位置 | 
| drawAttention() | 引起窗口注意 | 
| clearAttention() | 清除窗口的注意 | 
| close() | 关闭窗口 | 
| show() | 显示窗口 | 
| hide() | 隐藏窗口 | 
| setAlwaysOnTop  (boolean onTop) | 配置窗口是否应始终 显示在最上方 | 
| isAlwaysOnTop() | 标识窗口是否始终显示在最上方 | 
| setInterceptAllKeys  (boolean intercept) | 配置窗口是否应接收 所有按键事件 | 
| setVisibleOnAllWorkspaces  (boolean visible) | 配置窗口是否应显示在 所有工作区 | 
这些属性易于使用和理解。contentWindow 属性很有趣,因为它提供了对底层 JavaScript Window 的访问。这允许后台脚本访问常见的 JavaScript Window 属性,例如 document、setTimeout() 和 alert()。
除了提供表 3 中的函数外,AppWindow 还提供事件,使脚本能够响应窗口的事件。表 4 列出了这些事件以及触发它们的事件。
表 4:AppWindow 事件
| 事件 | 触发器 | 
|---|---|
| onBoundsChanged | 窗口边界更改时触发 | 
| onClosed | 窗口关闭时触发 | 
| onFullscreened | 窗口设置为占据屏幕时触发 | 
| onMaximized | 窗口最大化时触发 | 
| onMinimized | 窗口最小化时触发 | 
| onRestored | 窗口恢复到其原始边界时触发 | 
每个窗口事件都有一个名为 addListener 的属性,它接受一个回调函数,在事件发生时调用。在所有情况下,此回调函数都不会接收任何参数。例如,当窗口移动或调整大小时,以下代码会将消息打印到控制台:
chrome.app.window.create("window.html", 
  {...},
  function(win) {
    win.onBoundsChanged.addListener(function() {
      console.log("Bounds changed");
    });
  }
);
在此示例中,窗口的内容由 window.html 确定。此 HTML 文档可以包含一个或多个 JavaScript 文件,称为*窗口脚本*。窗口脚本可以访问与后台脚本相同的能力,下一节将介绍一个访问用户文件系统的窗口脚本。
4. 文件系统接口
大多数 Web 应用无法与用户的文件或系统硬件交互。但 Chrome 应用可以通过 chrome.fileSystem API 访问客户端的文件系统。此功能强大但复杂。因此,我将讨论分为四个部分:
- 获取权限
- chooseEntry 函数
- File API
- chrome.fileSystem API
本节的最后一部分将介绍一个示例应用,该应用将文件内容翻倍。也就是说,它从文件中读取文本并将其写入文件末尾。
4.1 获取权限
要访问用户的文件系统,应用必须通过向 manifest 的 permissions 数组添加一个或多个值来请求权限:
- permissions: ["fileSystem"]— 请求只读权限
- permissions: [{"fileSystem": ["write"]}]— 请求读/写权限
- permissions: [{"fileSystem": ["write", "retainEntries", "directory"]}]— 请求读取、写入、保留文件以及访问目录(除文件外)的权限
如果授予了权限,脚本就可以访问用户的文件。如果未授予,尝试访问用户文件系统将导致错误。
4.2 chooseEntry 函数
在 chrome.fileSystem API 中,chooseEntry 函数会打开一个对话框,让用户选择现有文件/目录或识别新文件/目录。其签名如下:
chooseEntry(Object opts, Function callback)
第一个参数是一个对象,用于配置对话框的外观和行为。它有五个可选属性:
- type— 文件对话框类型(- openFile、- openWritableFile、- saveFile或- openDirectory)
- suggestedName— 要打开文件的建议名称
- accepts— 一个对象数组,用于过滤可选择的文件。每个对象有三个可选属性:- description、- mimeTypes和- extensions
- acceptsAllTypes— 对话框是否接受所有类型的文件
- acceptsMultiple— 对话框是否接受多选
chooseEntry 的第二个参数是一个回调函数,该函数接收用户选择的结果。选择结果以 Entry 或 FileEntry 对象数组的形式提供。
一个例子可以阐明 chooseEntry 的工作原理。以下代码在窗口脚本中执行,当用户单击 ID 为 browse 的按钮时,会显示一个文件选择对话框。
document.getElementById("browse").onclick = function() {
  chrome.fileSystem.chooseEntry( {
    type: "openFile",
    accepts: [ { description: "Data files (*.dat)",
                 extensions: ["dat"]} ]
  }, function(entry) {
    console.log("Entry name: " + entry.name);
  });
}
对话框的外观和初始搜索目录取决于用户的操作系统。图 3 显示了在我的 Windows 7 机器上生成的对话框外观:
图 3:由 chooseEntry 创建的文件选择对话框

如果设置了 acceptsAllTypes 为 true,对话框将允许用户选择任何文件,无论其扩展名是什么。如果设置了 acceptsMultiple 为 true,对话框将允许用户选择多个文件。但由于未设置这两个属性,用户只能打开一个 *.dat 文件。通过将 extensions 字段设置为 ["dat"] 来配置必需的后缀。
当用户选择文件时,示例回调函数会接收一个相应的 Entry 并使用 name 属性获取文件的名称。要使用这些 Entry 对象,您需要熟悉 Google 的 File API。
4.3 File API
在 HTML5 开发过程中,Google 提出了一个 File API,它允许 Web 应用程序访问用户的文件。您可以在此处阅读规范。File API 未包含在 HTML5 中,但 chrome.fileSystem API 依赖它来与客户端的文件系统进行交互。本讨论将介绍 File API 的五个对象:Entry、FileEntry、File、FileReader 和 FileWriter。
根据 File API,文件由 FileEntry 对象表示,目录由 DirectoryEntry 对象表示。它们都继承自 Entry 类型,其属性列在表 5 中。
表 5:Entry 属性
| 函数 | 描述 | 
|---|---|
| 名称 | 条目的名称 | 
| fullPath | 条目的完整路径 | 
| filesystem | 包含该条目的文件系统 | 
| isFile | 标识该条目是否为文件 | 
| isDirectory | 标识该条目是否为目录 | 
| toURL(String mimeType) | 返回条目的 URL | 
| getMetadata  (Function successCallback,   Function errorCallback) | 提供条目的最后修改日期 | 
| getParent  (Function successCallback,   Function errorCallback) | 提供条目的父目录 | 
| 移除  (Function successCallback,   Function errorCallback) | 从文件系统中删除该条目 | 
| moveTo(DirectoryEntry parent,  String newName,  Function successCallback,  Function errorCallback) | 将条目移动到指定目录 | 
| copyTo(DirectoryEntry parent,   String newName,  Function successCallback,  Function errorCallback) | 将条目复制到指定目录 | 
其中许多函数接受两个回调函数:一个在操作成功完成时调用,另一个在发生错误时调用。以下代码演示了其工作原理。当用户在对话框中选择条目时,remove 函数会将其从文件系统中删除并向日志打印一条消息。如果发生错误,应用将记录一条错误消息。
chrome.fileSystem.chooseEntry( {
  type: "openFile",
  accepts: [ { description: "Text files (*.text)",
               extensions: ["txt"]} ]
  }, function(entry) {
    entry.remove(
      function() {console.log("Deletion successful")},
      function() {console.log("Error occurred")}
    );
});
FileEntry 表示用户文件系统中的一个文件,它具有表 5 中未列出的两个函数:
- file(Function successCallback, Function errorCallback)— 提供条目底层的- File对象
- createWriter(Function successCallback, Function errorCallback)— 提供一个能够写入文件的- FileWriter
通过访问 FileEntry 的 File,应用可以使用 FileReader 读取文件内容。同样,FileWriter 允许将数据写入文件。以下小节将讨论这两种功能。
4.3.1 从文件中读取数据
Google 的 File API 定义了一个 FileReader 对象,它能够读取 File 中的数据。通常,使用此对象需要四个步骤:
- 使用其构造函数创建一个新的 FileReader:var reader = new FileReader();
- 调用 FileReader的三个读取函数之一。
- 为 FileReader完成读取数据时响应的函数赋值。
- 在函数内部,通过 FileReader的result属性访问数据。
对于第二个步骤,FileReader 对象有三个读取数据的函数:
- readAsArrayBuffer(File f)— 以- ArrayBuffer的形式返回文件内容(用于二进制数据)
- readAsText(File f, String encoding)— 以字符串形式返回文件内容
- readAsDataURL(File f)— 以 Base64 编码的字符串形式返回文件内容
FileReader 具有许多与事件相关的属性,onload 属性用于标识在读取器完成从文件加载数据时调用的函数。以下代码演示了如何使用它:
// Create the new FileReader
var reader = new FileReader();
// Assign a function to be called when the read is complete
reader.onload = function() {
  console.log("Result: " + reader.result);
}
// Call the read function when the File is available
entry.file(function(f) {
  reader.readAsText(f);
}
准确地说,FileReader 的读取函数操作的是 Blob,而 File 是 Blob 的一种。以下讨论将更详细地解释 Blob。
4.3.2 向文件写入数据
FileEntry 对象的 createWriter 函数提供了一个 FileWriter,它能够将数据写入底层文件。它比 FileReader 更易于使用,表 6 列出了它的属性。
表 6:FileWriter 属性
| 函数 | 描述 | 
|---|---|
| length | 文件的字节长度 | 
| 位置 | 文件中的当前写入位置 | 
| readyState | 写入器的状态( INIT、WRITING或DONE) | 
| error | 错误条件 | 
| write(Blob data) | 将数据写入文件 | 
| seek(long long offset) | 将写入位置移动到指定位置 | 
| truncate(unsigned long long size) | 将文件长度更改为指定大小 | 
| abort() | 终止写入操作 | 
write 函数接受一个 Blob。根据文档,Blob 是“一个类似文件的、不可变的原始数据对象”。关于 Blob 需要知道的重要一点是,Blob 构造函数接受一个字符串、Blob、ArrayBuffer、ArrayBufferView 数组,或这些对象的任何组合。例如,以下代码将一个字符串写入与 FileWriter 关联的文件:
writer.write(new Blob(["Example text"]);
与 FileReader 类似,FileWriter 提供可以分配给函数的事件。如果一个函数被设置为等于写入器的 onwriteend 事件,则在写入操作完成后将调用该函数。如果一个函数被设置为等于写入器的 onerror 事件,则在发生错误时将调用该函数。如果 entry 是一个 FileEntry,则以下代码演示了如何创建 FileWriter 并将 Blob 写入相应的文件:
// Write the received data to the file
entry.createWriter(function(writer) {
  // Called when the write operation is complete
  writer.onwriteend = function() {
    console.log("Write finished.");
  };
  // Called if an error occurs
  writer.onerror = function(error) {
    console.log("Write error: " + error.toString());
  };
  // Create a new Blob and write its text to the file
  writer.write(new Blob(["Example text"]));
});
默认情况下,文本将写入文件开头。要更改文本的写入位置,应用应调用 FileWriter 的 seek 函数并提供所需的偏移量。下一个示例应用演示了如何实现这一点。
4.4 chrome.fileSystem API
除了前面讨论过的 chooseEntry 函数外,chrome.fileSystem API 还提供了许多其他函数。表 7 列出了 chooseEntry 和其他六个函数。
表 7:chrome.fileSystem 的函数(节选)
| 函数 | 描述 | 
|---|---|
| chooseEntry(Options opts,  Function callback) | 创建文件选择对话框 | 
| getDisplayPath(Entry entry,  Function callback) | 为给定的条目提供完整路径 | 
| getWritableEntry(Entry entry,   Function callback) | 为给定的条目提供一个可写入的条目 | 
| isWritableEntry(Entry entry,  Function callback) | 标识应用是否有权限 写入该条目 | 
| retainEntry(Entry entry) | 返回条目的字符串标识符 | 
| restoreEntry(String id,  Function callback)  | 根据其标识符访问条目 | 
| isRestorable(String id,  Function callback) | 标识应用是否有权限 根据其标识符恢复条目 | 
在大多数这些函数中,第二个参数是在操作完成时调用的回调函数。例如,getWritableEntry 的第二个参数是一个回调函数,如果应用有权写入该条目,它将提供该 Entry。
如果应用需要操作多个 Entry 对象,它可以为每个对象分配一个标识符。retainEntry 返回一个 Entry 的标识符,然后可以使用 restoreEntry 根据标识符访问 Entry 对象。
4.5 示例应用程序
在示例存档中,file_demo 应用中的代码展示了如何读取和写入文件。它包含一个后台脚本 background.js,该脚本创建一个 200x200 的窗口作为应用的 UI。其代码如下:
// Responds when the app is launched
chrome.app.runtime.onLaunched.addListener(function() {
  
  // Create the window
  chrome.app.window.create(
    "window.html", 
    { "outerBounds": {"width": 200, "height": 200} 
  });
});
窗口的外观由 window.html 文件定义,其标记如下:
<html>
  <body>
    <button id="browse">Select File</button>
    <script src="scripts/window.js"></script>  
  </body>
</html>
这会创建一个按钮并将 JavaScript 代码注入到 window.js 脚本中。当单击按钮时,此代码会调用 chooseEntry 以允许用户选择一个文本文件。脚本代码如下:
document.getElementById("browse").onclick = function() {
  
  // Open a dialog box
  chrome.fileSystem.chooseEntry( {
    type: "openFile",
    accepts: [ { description: "Text files (*.txt)",
                 extensions: ["txt"]} ]
  }, function(entry) {
    // Create a FileReader
    var reader = new FileReader();
    // Called when the read is complete
    reader.onload = function() {
      
      // Create a FileWriter
      entry.createWriter(function(writer) {
        
        // Write to the end of the file
        writer.seek(writer.length);        
        
        // Log a message when the write operation is completed
        writer.onwriteend = function() {
          console.log("Write successful");
        };
        // Log a message if an error occurs       
        writer.onerror = function(error) {
          console.log("Write error: " + error.toString());
        };
        // Create a Blob and write its text to the file
        writer.write(new Blob([reader.result]));
      });
    }
    // Call the read function when the File is available
    entry.file(function(f) {
      reader.readAsText(f);
    });
  });
}
用户选择文件后,应用会创建一个 FileReader 并将其 onload 属性与一个匿名函数关联起来。当读取文件文本时,此函数会创建一个 FileWriter 并调用其 seek 函数跳到文件末尾。脚本将文件文本写入文件末尾,如果操作成功完成,则会在控制台打印一条消息。
5. 文本转语音
从 Chrome 浏览器 14 版本开始,它就包含了一个文本转语音引擎。如果应用 manifest 的 permissions 数组包含 "tts",则应用可以使用 chrome.tts API 访问语音引擎。表 8 列出了该 API 的函数并提供了每个函数的描述。
表 8:chrome.tts 的函数
| 函数 | 描述 | 
|---|---|
| speak(String utterance,  Object options,  Function callback) | 生成并发出给定文本的语音 | 
| getVoices(Function callback) | 返回可用语音的集合 | 
| stop() | 停止正在进行的语音 | 
| pause() | 暂停正在进行的语音 | 
| resume() | 暂停后恢复朗读 | 
| isSpeaking(Function callback)  | 标识引擎当前是否正在说话 | 
顾名思义,speak 函数从文本生成语音并发出语音。唯一的必需参数是第一个,它标识要发出的文本。以下代码发出浏览器的名称:
chrome.tts.speak("Chrome");
第三个参数标识在语音完成之前调用的函数。speak 的第二个参数是一个对象,用于配置语音的生成方式。其所有属性都是可选的:
- voiceName— 要使用的语音名称
- lang— 要使用的语言
- gender— 所需性别(- male或- female)
- rate— 相对于默认语速的语速(2 - 两倍速,0.5 - 半速)
- pitch— 相对于默认音高的音高(2 - 更高音高,0 - 更低音高)
- volume— 说话音量(0 到 1 之间)
- enqueue— 如果为 true,则将语音添加到当前语音的队列中;如果为 false(默认),则中断当前语音
- extensionId— 包含所需语音引擎的扩展程序 ID
- requiredEventTypes— 必须支持的事件类型
- desiredEventTypes— 感兴趣的事件类型
- onEvent— 在期望的事件上调用的函数
例如,以下代码以快速的英式男声说话:
chrome.tts.speak("It's just a flesh wound", {voiceName: "Google UK English Male", rate: 1.5});
getVoices 提供一个包含所有可生成语音的数组。每个语音都由一个 TtsVoice 对象表示,以下小节将对此进行详细讨论。
5.1 TtsVoice
Chrome 应用可以使用多种不同的语音生成语音。每种语音都由一个 TtsVoice 表示,表 9 列出了其不同的属性。
表 9:TtsVoice 属性
| 函数 | 描述 | 
|---|---|
| voiceName | 语音的名称 | 
| lang | 语音的语言 | 
| gender | 语音的性别:男性或女性 | 
| remote | 该语音是否通过网络提供 | 
| extensionId | 提供此语音的扩展程序的 ID | 
| eventTypes | 语音可能触发的事件 | 
例如,以下代码使用 getVoices 访问 Chrome 的预装语音并显示其属性:
chrome.tts.getVoices(function(voices) {
  for (var i = 0; i < voices.length; i++) {
    console.log("Name: " + voices[i].voiceName);
    console.log("Language: " + voices[i].lang);
    console.log("Gender: " + voices[i].gender);
    console.log("Extension: " + voices[i].extensionId);
    console.log("Events: " + voices[i].eventTypes);
  }
});
当这段代码在 Chrome 51 中执行时,数组包含二十种语音。表 10 列出了十二种语音及其性别、语言和支持的事件。
表 10:内置语音
| 语音名称 | 性别/语言 | 事件 | 
|---|---|---|
| Google 美国英语 | female/en-US | start,end,interrupted,cancelled,error | 
| Google 英国英语男声 | male/en-GB | start,end,interrupted,cancelled,error | 
| Google 英国英语女声 | female/en-GB | start,end,interrupted,cancelled,error | 
| Google 西班牙语 | female/es-ES | start,end,interrupted,cancelled,error | 
| Google 美国西班牙语 | female/en-US | start,end,interrupted,cancelled,error | 
| Google 法语 | female/fr-FR | start,end,interrupted,cancelled,error | 
| Google 德语 | female/de-DE | start,end,interrupted,cancelled,error | 
| Google 意大利语 | female/it-IT | start,end,interrupted,cancelled,error | 
| Google 印尼语 | female/id-ID | start,end,interrupted,cancelled,error | 
| Google 荷兰语 | female/nl-NL | start,end,interrupted,cancelled,error | 
| Google 波兰语 | female/pl-PL | start,end,interrupted,cancelled,error | 
| Google 巴西葡萄牙语 | female/pt-BR | start,end,interrupted,cancelled,error | 
当应用选择一种语音进行文本转语音生成时,该语音决定了应用可以响应哪些语音事件。事件处理是通过在 speak 第二个参数中设置 desiredEventTypes 属性并通过将函数分配给 onEvent 属性来配置的。
5.2 示例代码
在本篇文章的示例代码中,tts_demo 展示了 Chrome 应用如何将文本转换为语音。窗口提供了用于设置发音内容和选择语音的控件。图 4 显示了该窗口的外观。
图 4:tts_demo 应用窗口

当按下 **说话** 按钮时,将调用 speak 函数,并将文本框中的文本作为参数。语音的名称由组合框的选择决定。项目 scripts/window.js 中的代码显示了如何实现这一点:
var voiceselect = document.getElementById("voiceselect");
// Access available voices
chrome.tts.getVoices(function(voices) {
  // Add voice options
  for (var i = 0; i < voices.length; i++) {
    voiceselect.options[i] = new Option(voices[i].voiceName, i);
  }
  voiceselect.selectedIndex = 2;
});
// Stop the speech
document.getElementById("stop").onclick = function() {
  
  // Stop if speaking
  chrome.tts.isSpeaking(function(speaking) {
    if (speaking) {
      chrome.tts.stop();
    }
  });
}
// Speak in the desired language
document.getElementById("speak").onclick = function() {
  
  var text = document.getElementById("utterance").value;
  var voice = voiceselect.options[voiceselect.selectedIndex].text;
  chrome.tts.speak(text, {voiceName: voice});
}
这演示了如何使用 isSpeaking 和 stop 函数。如果用户单击停止图像,应用将调用 isSpeaking 以确保引擎当前正在生成语音。如果回调函数提供的值为 true,则应用将调用 stop 函数。这不仅会停止当前的语音,还会清除队列中任何待定的发音内容。
历史
2016 年 6 月 17 日 - 首次提交文章


