65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (5投票s)

2016年6月17日

CPOL

21分钟阅读

viewsIcon

138146

downloadIcon

215

本文介绍如何编写在 Chrome 浏览器中运行的应用。

1. 引言

上一篇文章讨论了 Chrome 扩展程序,而本文则侧重于 Chrome 应用的开发。这两者有很多共同之处,它们之间的区别乍一看可能显得很微妙。为了帮助理解,先来区分三个术语:

  1. 软件应用程序 —— 用于帮助用户执行某项活动的软件,例如处理文本、编辑图像或玩游戏。通常具有专用用户界面。
  2. Web 应用 —— 在浏览器中运行的软件应用程序(应用程序中的应用程序)。
  3. 浏览器扩展程序 —— 用于自定义浏览体验或增强浏览器功能的软件。很少有用户界面。

Chrome 应用(通常简称 应用)是在 Chrome 浏览器中运行的 Web 应用。对用户而言,它的外观和感觉与普通 Web 应用无异,但它可以访问普通应用程序无法触及的丰富功能。缺点是 Chrome 应用只能在 Chrome 中运行。

从开发者的角度来看,Chrome 应用和扩展程序之间的区别很明显。在扩展程序中,内容脚本提供对 Web 文档的访问,弹出窗口提供基本的用户界面。应用没有内容脚本或弹出窗口,但用户界面要强大得多——应用可以在独立于浏览器之外的窗口中运行。

除了用户界面,应用相对于扩展程序的另一个优势在于对用户系统的底层访问。Chrome 应用可以访问硬件功能,包括蓝牙、TCP/UDP 套接字和通用串行总线 (USB)。它们还可以读取和写入用户的文件系统。我个人最喜欢的功能是 tts(文本转语音),它允许应用生成多种不同语言的语音。

本文将介绍如何编写创建窗口、访问用户文件系统以及将文本转换为语音的应用。但在深入研究代码之前,理解 Chrome 应用的结构以及它在浏览器中启动的方式非常重要。这些主题将在下一节中讨论。

2. 应用开发基础

Chrome 应用的文件结构几乎与扩展程序相同。需要注意三个基本特征:

  1. 应用的功能和能力必须在名为 manifest.json 的文件中定义。
  2. 应用可以有后台脚本,但不能有内容脚本或弹出窗口。
  3. 与扩展程序一样,应用可以通过 Chrome 开发者仪表板进行管理,网址为 chrome://extensions。

本节将首先讨论 manifest.json 的格式,然后介绍安装和启动一个简单应用的流程。

2.1 manifest.json

应用的 manifest 接受许多与扩展程序 manifest 相同的属性。这些属性包括 manifest_versionnamedescriptionversioniconspermissions。主要区别在于应用的 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 中,请按照以下四个步骤操作:

  1. 下载 example_code.zip 并解压存档。
  2. 在 Chrome 中,通过导航到 chrome://extensions 打开 Chrome 开发者仪表板。
  3. 按 **加载已解压的扩展程序...** 按钮并选择 basic_app 文件夹。
  4. 按确定加载应用。

当扩展程序加载后,将在仪表板的扩展程序列表中添加一个条目。图 2 显示了它的样子。

图 2:Chrome 开发者仪表板中的新条目

注意到 **启动** 链接很重要。与扩展程序不同,应用在安装时不会开始执行。应用必须被显式启动。

**重新加载** 链接会将应用重新加载到 Chrome 中。底部 **后台页面** 链接会打开应用的后台页面。这在您想查看与后台脚本关联的控制台时很有用。

3. chrome.app.runtime 和 chrome.app.window

应用和扩展程序的主要区别在于,应用可以在自己的窗口中执行。要创建这些窗口,应用必须调用两个 API 的函数:chrome.app.runtimechrome.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 — 框架类型:nonechrome(默认为 chrome
  • state — 窗口的初始状态:normalfullscreenmaximizedminimized

第二个和第三个属性是 BoundsSpecificationBoundsSpecification 有八个属性用于定义位置和大小:lefttopwidthheightminWidthminHeightmaxWidthmaxHeight。例如,以下代码创建一个 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 属性,例如 documentsetTimeout()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 访问客户端的文件系统。此功能强大但复杂。因此,我将讨论分为四个部分:

  1. 获取权限
  2. chooseEntry 函数
  3. File API
  4. 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 — 文件对话框类型(openFileopenWritableFilesaveFileopenDirectory
  • suggestedName — 要打开文件的建议名称
  • accepts — 一个对象数组,用于过滤可选择的文件。每个对象有三个可选属性:descriptionmimeTypesextensions
  • acceptsAllTypes — 对话框是否接受所有类型的文件
  • acceptsMultiple — 对话框是否接受多选

chooseEntry 的第二个参数是一个回调函数,该函数接收用户选择的结果。选择结果以 EntryFileEntry 对象数组的形式提供。

一个例子可以阐明 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 创建的文件选择对话框

如果设置了 acceptsAllTypestrue,对话框将允许用户选择任何文件,无论其扩展名是什么。如果设置了 acceptsMultipletrue,对话框将允许用户选择多个文件。但由于未设置这两个属性,用户只能打开一个 *.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 的五个对象:EntryFileEntryFileFileReaderFileWriter

根据 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

通过访问 FileEntryFile,应用可以使用 FileReader 读取文件内容。同样,FileWriter 允许将数据写入文件。以下小节将讨论这两种功能。

4.3.1 从文件中读取数据

Google 的 File API 定义了一个 FileReader 对象,它能够读取 File 中的数据。通常,使用此对象需要四个步骤:

  1. 使用其构造函数创建一个新的 FileReadervar reader = new FileReader();
  2. 调用 FileReader 的三个读取函数之一。
  3. FileReader 完成读取数据时响应的函数赋值。
  4. 在函数内部,通过 FileReaderresult 属性访问数据。

对于第二个步骤,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,而 FileBlob 的一种。以下讨论将更详细地解释 Blob

4.3.2 向文件写入数据

FileEntry 对象的 createWriter 函数提供了一个 FileWriter,它能够将数据写入底层文件。它比 FileReader 更易于使用,表 6 列出了它的属性。

表 6:FileWriter 属性
函数 描述
length 文件的字节长度
位置 文件中的当前写入位置
readyState 写入器的状态(INITWRITINGDONE
error 错误条件
write(Blob data) 将数据写入文件
seek(long long offset) 将写入位置移动到指定位置
truncate(unsigned long long size) 将文件长度更改为指定大小
abort() 终止写入操作

write 函数接受一个 Blob。根据文档,Blob 是“一个类似文件的、不可变的原始数据对象”。关于 Blob 需要知道的重要一点是,Blob 构造函数接受一个字符串、BlobArrayBufferArrayBufferView 数组,或这些对象的任何组合。例如,以下代码将一个字符串写入与 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"]));
});

默认情况下,文本将写入文件开头。要更改文本的写入位置,应用应调用 FileWriterseek 函数并提供所需的偏移量。下一个示例应用演示了如何实现这一点。

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 — 所需性别(malefemale
  • 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});
}

这演示了如何使用 isSpeakingstop 函数。如果用户单击停止图像,应用将调用 isSpeaking 以确保引擎当前正在生成语音。如果回调函数提供的值为 true,则应用将调用 stop 函数。这不仅会停止当前的语音,还会清除队列中任何待定的发音内容。

历史

2016 年 6 月 17 日 - 首次提交文章

© . All rights reserved.