diff --git a/files/zh-cn/web/javascript/reference/global_objects/string/index.md b/files/zh-cn/web/javascript/reference/global_objects/string/index.md index cec409f76c0512..4b343b25d422bf 100644 --- a/files/zh-cn/web/javascript/reference/global_objects/string/index.md +++ b/files/zh-cn/web/javascript/reference/global_objects/string/index.md @@ -177,9 +177,6 @@ console.log(eval(s2.valueOf())); // returns the number 4 - : **通过一串 Unicode 创建字符串。** - {{jsxref("String.fromCodePoint()")}} {{experimental_inline}} - : 通过一串 码点 创建字符串。 - - - - {{jsxref("String.raw()")}} {{experimental_inline}} - : 通过模板字符串创建字符串。 diff --git a/files/zh-cn/web/javascript/reference/operators/remainder/index.md b/files/zh-cn/web/javascript/reference/operators/remainder/index.md index 3151c236048bc6..e19e5bd17763cb 100644 --- a/files/zh-cn/web/javascript/reference/operators/remainder/index.md +++ b/files/zh-cn/web/javascript/reference/operators/remainder/index.md @@ -57,8 +57,6 @@ NaN % 2 // NaN ## 相关链接 - - - [Addition operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Addition) - [Subtraction operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Subtraction) - [Division operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Division) diff --git a/files/zh-cn/web/javascript/reference/statements/continue/index.md b/files/zh-cn/web/javascript/reference/statements/continue/index.md index 59a3b2355c5bc9..01cf7ae4f1c5c6 100644 --- a/files/zh-cn/web/javascript/reference/statements/continue/index.md +++ b/files/zh-cn/web/javascript/reference/statements/continue/index.md @@ -23,9 +23,6 @@ continue [ label ]; 与 {{jsxref("Statements/break", "break")}} 语句的区别在于, continue 并不会终止循环的迭代,而是: - 在 {{jsxref("Statements/while", "while")}} 循环中,控制流跳转回条件判断; - - - - 在 {{jsxref("Statements/for", "for")}} 循环中,控制流跳转到更新语句。 `continue` 语句可以包含一个可选的标号以控制程序跳转到指定循环的下一次迭代,而非当前循环。此时要求 `continue` 语句在对应的循环内部。 diff --git a/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.html b/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.html deleted file mode 100644 index 78a58db7ea10e2..00000000000000 --- a/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.html +++ /dev/null @@ -1,219 +0,0 @@ ---- -title: 添加到主屏幕 -slug: Web/Progressive_web_apps/Add_to_home_screen -tags: - - PWA - - 图标 - - 服务进程 - - 添加到主屏幕 - - 清单 - - 渐进式 Web 应用 -translation_of: Web/Progressive_web_apps/Add_to_home_screen -original_slug: Web/Progressive_web_apps/添加到主屏幕 ---- -

添加到主屏幕(Add to Home Screen,简称 A2HS)是现代智能手机浏览器中的一项功能,使开发人员可以轻松便捷地将自己喜欢的 Web 应用程序(或网站)的快捷方式添加到主屏幕中,以便用户随后可以通过单击访问它。本指南说明了 A2HS 的使用方式,以及作为开发人员为使用户能利用 A2HS 所需做的事情。

- -

为什么选择 A2HS?

- -

A2HS 被认为是渐进式 Web 应用程序哲学的一部分——为 Web 应用程序提供与原生应用程序相同的用户体验优势,因此它们可以在当今的生态系统战争中竞争。这部分是通过访问主屏幕上的应用程序图标来访问应用程序,然后将其整齐地显示在自己的窗口中的简单手势。A2HS 使这成为可能。

- -

哪些浏览器支持 A2HS?

- -

Mobile Chrome / Android Webview 从 31 版开始支持 A2HS,Opera for Android 从 32 版开始支持,Firefox for Android 从 58 版 开始支持。

- -

如何使用?

- -

我们已经编写了一个非常简单的示例网站(观看我们的在线演示,并查看源代码),该网站虽然功能不多,但是实现 A2HS 所必须的代码都有包含,Service Worker 也使其可以脱机使用。这个示例展示了一系列的狐狸图片。

- -

如果您有适用于 Android 的 Firefox,使用它打开我们的示例:https://mdn.github.io/pwa-examples/a2hs/。你将会看到狐狸图片,但更重要的是,你将会看到一个带有加号(+)的“主页”图标——这是为具有必要功能的任何站点显示的“添加到主屏幕”图标。

- -

地址栏的“添加到主屏幕”图标

- -

点击此按钮将显示一个确认横幅,按下大大的“添加到主屏幕”按钮即可将应用添加到主屏幕。(注意:在 Android 8 及更高版本中,将首先显示系统级的“添加到主屏幕”权限对话框。)

- -

确认横幅

- -

使用 Mobile Chrome 的体验则略有不同;加载我们的网站后,您会看到一个弹出安装横幅,询问您是否要将此应用添加到主屏幕。

- -

Mobile Chrome 上的 A2HS 提示

- -
-

注意:您可以在“Web App 安装横幅”一文中找到有关 Chrome 安装横幅的更多信息。

-
- -

如果您选择不将其添加到主屏幕,则可以稍后使用 Chrome 主菜单中的添加到主屏幕图标添加。

- -

无论使用哪种浏览器,当您选择将应用程序添加到主屏幕时,您都会看到它与短标题一起出现,就像原生应用程序一样。

- -

桌面上的 PWA 图标

- -

点按此图标可以将其打开,但是作为全屏应用程序,您将不再看到其周围的浏览器用户界面。

- -

如何使应用程序支持 A2HS?

- -

要将您的应用添加到主屏幕,它需要满足以下条件:

- - - -

Manifest

- -

Web manifest 以标准 JSON 格式编写,应放置在应用程序目录中的某个位置(最好是在根目录中),名称为 somefilename.webmanifest(这里我们将其命名为 manifest.webmanifest)。它包含多个字段,这些字段定义有关 Web 应用程序及其行为的某些信息。

- -
-

注意:.webmanifest 扩展名是在规范的“媒体类型注册”部分中指定的,但通常浏览器也支持带有其他适当扩展名的清单,例如 .json。

-
- -

A2HS 所需的字段如下:

- - - -

我们的示例应用程序的 manifest 如下所示:

- -
{
-  "background_color": "purple",
-  "description": "Shows random fox pictures. Hey, at least it isn't cats.",
-  "display": "fullscreen",
-  "icons": [
-    {
-      "src": "icon/fox-icon.png",
-      "sizes": "192x192",
-      "type": "image/png"
-    }
-  ],
-  "name": "Awesome fox pictures",
-  "short_name": "Foxes",
-  "start_url": "/pwa-examples/a2hs/index.html"
-}
- -

合适的图标

- -

如以上 manifest 所示,我们包括一个 192 x 192 像素的图标,供我们的应用使用。您可以根据需要添加更多尺寸;Android 将为每个不同的用例选择最合适的尺寸。您还可以决定添加不同类型的图标,以便设备可以使用他们能够使用的最佳图标(例如,Chrome 已经支持 WebP 格式)。

- -

请注意,每个图标对象中的 type 成员都指定了该图标的 MIME 类型,因此浏览器可以快速读取该图标的类型,并在不支持此类型时将其忽略并采用其他图标。

- -

在设计图标方面,您应该遵循与任何 Android 图标相同的最佳做法(请参阅 Android 图标设计指南)。

- -

将 HTML 链接到 manifest

- -

要完成 manifest 的设置,您需要从应用程序主页的 HTML 中引用它:

- -
<link rel="manifest" href="manifest.webmanifest">
- -

一旦有了 manifest 声明,支持 A2HS 的浏览器就会知道在哪里查找它。

- -

A2HS 不提供什么?

- -

请记住,将应用程序添加到主屏幕时,它只会使该应用程序易于访问,而不会将应用程序的资料和数据下载到您的设备上,也不会使该应用程序脱机使用或类似的操作。为了使应用离线运行,你必须使用 Service Worker API 来离线存储资源,如果需要,还可以使用 Web StorageIndexedDB 来存储其数据。

- -

在示例应用程序中,我们仅使用了一个 service worker 来存储所有必需的文件。service worker 使用index.js 文件中的最终的代码块在网站上注册。然后,我们使用 Cache API 缓存网站的所有资产,并使用 sw.js 文件中的代码从缓存而不是网络中为它们提供服务。

- -

桌面上的_A2HS

- -

虽然 PWA 最初旨在改善移动操作系统上的用户体验,但也有人推进在桌面端安装 PWA 的进程。

- -
-

注意:在撰写本文时,仅在较新版本的 Chrome 支持以下功能(在 Windows 上默认支持,在 macOS 则需开启 #enable-desktop-pwas 标志)。

-
- -

添加安装按钮

- -

为了使 PWA 可在桌面上安装,我们首先在文档中添加了一个按钮,以允许用户进行安装—桌面应用程序不会自动提供此按钮,并且需要通过用户手势来触发安装:

- -
<button class="add-button">Add to home screen</button>
- -

然后,我们给它一些简单的样式:

- -
.add-button {
-  position: absolute;
-  top: 1px;
-  left: 1px;
-}
- -

用于处理安装的_JavaScript

- -

index.js 文件的底部,我们添加了一些 JavaScript 代码来处理安装。首先,我们声明一个 deferredPrompt 变量(我们将在后面解释),获得对安装按钮的引用,并初始设置为 display: none

- -
let deferredPrompt;
-const addBtn = document.querySelector('.add-button');
-addBtn.style.display = 'none';
- -

我们最初隐藏该按钮是因为 PWA 必须满足 A2HS 标准才会安装。条件满足时,支持的浏览器将触发 beforeinstallprompt 事件。然后,我们可以使用以下处理程序来处理安装:

- -
window.addEventListener('beforeinstallprompt', (e) => {
-  // 防止 Chrome 67 及更早版本自动显示安装提示
-  e.preventDefault();
-  // 稍后再触发此事件
-  deferredPrompt = e;
-  // 更新 UI 以提醒用户可以将 App 安装到桌面
-  addBtn.style.display = 'block';
-
-  addBtn.addEventListener('click', (e) => {
-    // 隐藏显示 A2HS 按钮的界面
-    addBtn.style.display = 'none';
-    // 显示安装提示
-    deferredPrompt.prompt();
-    // 等待用户反馈
-    deferredPrompt.userChoice.then((choiceResult) => {
-        if (choiceResult.outcome === 'accepted') {
-          console.log('User accepted the A2HS prompt');
-        } else {
-          console.log('User dismissed the A2HS prompt');
-        }
-        deferredPrompt = null;
-      });
-  });
-});
- -

所以我们在这里:

- - - -

点击处理程序包含以下步骤:

- - - -

于是,点击按钮后安装提示就会消失。

- -

应用安装提示

- -

如果用户选择安装,则将安装该应用程序(可作为独立的桌面应用程序使用),并且不再显示“安装”按钮(如果已经安装了该应用程序,则将不再触发 onbeforeinstallprompt 事件)。当您打开应用程序时,它将显示在其自己的窗口中:

- -

安装后的应用界面

- -

如果用户选择“取消”,则应用程序的状态将返回到单击按钮之前的状态。

- -
-

注意:本部分的代码主要来自 Pete LaPage 的应用安装横幅/添加到主屏幕

-
- -

其他

- - - -
{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}
diff --git a/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.md b/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.md new file mode 100644 index 00000000000000..5123f19426ac58 --- /dev/null +++ b/files/zh-cn/web/progressive_web_apps/add_to_home_screen/index.md @@ -0,0 +1,213 @@ +--- +title: 添加到主屏幕 +slug: Web/Progressive_web_apps/Add_to_home_screen +tags: + - PWA + - 图标 + - 服务进程 + - 添加到主屏幕 + - 清单 + - 渐进式 Web 应用 +translation_of: Web/Progressive_web_apps/Add_to_home_screen +original_slug: Web/Progressive_web_apps/添加到主屏幕 +--- +添加到主屏幕(Add to Home Screen,简称 A2HS)是现代智能手机浏览器中的一项功能,使开发人员可以轻松便捷地将自己喜欢的 Web 应用程序(或网站)的快捷方式添加到主屏幕中,以便用户随后可以通过单击访问它。本指南说明了 A2HS 的使用方式,以及作为开发人员为使用户能利用 A2HS 所需做的事情。 + +## 为什么选择 A2HS? + +A2HS 被认为是渐进式 [Web 应用程序](/zh-CN/docs/Web/Progressive_web_apps)哲学的一部分——为 Web 应用程序提供与原生应用程序相同的用户体验优势,因此它们可以在当今的生态系统战争中竞争。这部分是通过访问主屏幕上的应用程序图标来访问应用程序,然后将其整齐地显示在自己的窗口中的简单手势。A2HS 使这成为可能。 + +## 哪些浏览器支持 A2HS? + +Mobile Chrome / Android Webview 从 31 版开始支持 A2HS,Opera for Android 从 32 版开始支持,Firefox for Android 从 [58 版](/zh-CN/docs/Mozilla/Firefox/Releases/58) 开始支持。 + +## 如何使用? + +我们已经编写了一个非常简单的示例网站([观看我们的在线演示](https://mdn.github.io/pwa-examples/a2hs/),并[查看源代码](https://github.com/mdn/pwa-examples/tree/master/a2hs)),该网站虽然功能不多,但是实现 A2HS 所必须的代码都有包含,Service Worker 也使其可以脱机使用。这个示例展示了一系列的狐狸图片。 + +如果您有适用于 Android 的 Firefox,使用它打开我们的示例:`https://mdn.github.io/pwa-examples/a2hs/`。你将会看到狐狸图片,但更重要的是,你将会看到一个带有加号(+)的“主页”图标——这是为具有必要功能的任何站点显示的“添加到主屏幕”图标。 + +![地址栏的“添加到主屏幕”图标](add-to-home-screen-icon.png) + +点击此按钮将显示一个确认横幅,按下大大的“添加到主屏幕”按钮即可将应用添加到主屏幕。(注意:在 Android 8 及更高版本中,将首先显示系统级的“添加到主屏幕”权限对话框。) + +![确认横幅](https://mdn.mozillademos.org/files/15772/fx-a2hs-banner.png) + +使用 Mobile Chrome 的体验则略有不同;加载我们的网站后,您会看到一个弹出安装横幅,询问您是否要将此应用添加到主屏幕。 + +![Mobile Chrome 上的 A2HS 提示](https://mdn.mozillademos.org/files/15771/chrome-a2hs-banner.png) + +> **备注:** 您可以在“[Web App 安装横幅](https://developers.google.com/web/fundamentals/app-install-banners/)”一文中找到有关 Chrome 安装横幅的更多信息。 + +如果您选择不将其添加到主屏幕,则可以稍后使用 Chrome 主菜单中的添加到主屏幕图标添加。 + +无论使用哪种浏览器,当您选择将应用程序添加到主屏幕时,您都会看到它与短标题一起出现,就像原生应用程序一样。 + +![桌面上的 PWA 图标](a2hs-on-home-screen.png) + +点按此图标可以将其打开,但是作为全屏应用程序,您将不再看到其周围的浏览器用户界面。 + +## 如何使应用程序支持 A2HS? + +要将您的应用添加到主屏幕,它需要满足以下条件: + +- 应用通过 HTTPS 提供服务——Web 正朝着更加安全的方向发展,包括 A2HS 在内的许多现代 Web 技术都将仅工作在安全的环境中。 +- 从 HTML 头链接具有正确字段的 manifest 文件。 +- 有合适的图标可显示在主屏幕上。 +- Chrome 浏览器还要求该应用程序注册一个 Service Worker(这样在离线状态下就也可以运行)。 + +### Manifest + +Web manifest 以标准 JSON 格式编写,应放置在应用程序目录中的某个位置(最好是在根目录中),名称为 `somefilename.webmanifest`(这里我们将其命名为 `manifest.webmanifest`)。它包含多个字段,这些字段定义有关 Web 应用程序及其行为的某些信息。 + +> **备注:** .webmanifest 扩展名是在规范的“[媒体类型注册](https://w3c.github.io/manifest/#media-type-registration)”部分中指定的,但通常浏览器也支持带有其他适当扩展名的清单,例如 .json。 + +A2HS 所需的字段如下: + +- `background_color`:指定在某些应用程序上下文中使用的背景色。与 A2HS 最相关的一个是在点击主屏幕上的应用程序图标并首次开始加载时显示的初始屏幕(目前仅在通过 Chrome 将应用添加到主屏幕时显示)。 +- `display`:指定应如何显示应用。 为了使它看起来像一个应用程序而非网页,这里应该填写像是 `fullscreen` (根本不显示任何 UI)或 `standalone`(与前者非常相似,但是状态栏这样的系统级 UI 元素可能是可见的)的值。 +- `icons`:指定在不同位置(例如,在任务切换器上或更重要的是在主屏幕上)表示应用程序时浏览器使用的图标。 我们的演示中仅包含一个。 +- `name`/`short_name`:这些字段提供了在不同位置表示应用程序时要显示的应用程序名称。`name` 提供完整的应用名称。`short_name` 则是当没有足够的空间显示全名时,提供的缩写名称。如果您的应用程序名称特别长,建议您同时提供两者。 +- `start_url`:提供启动添加到主屏幕应用程序时应加载的资源的路径。请注意,这必须是一个相对 manifest 指向网站主页的相对路径。另请注意,Chrome 需要这条字段才能显示安装标语,而 Firefox 即使没有这条字段也会显示用于添加到主屏的含加号的主页图标。 + +我们的示例应用程序的 manifest 如下所示: + +```js +{ + "background_color": "purple", + "description": "Shows random fox pictures. Hey, at least it isn't cats.", + "display": "fullscreen", + "icons": [ + { + "src": "icon/fox-icon.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "name": "Awesome fox pictures", + "short_name": "Foxes", + "start_url": "/pwa-examples/a2hs/index.html" +} +``` + +### 合适的图标 + +如以上 manifest 所示,我们包括一个 192 x 192 像素的图标,供我们的应用使用。您可以根据需要添加更多尺寸;Android 将为每个不同的用例选择最合适的尺寸。您还可以决定添加不同类型的图标,以便设备可以使用他们能够使用的最佳图标(例如,Chrome 已经支持 WebP 格式)。 + +请注意,每个图标对象中的 `type` 成员都指定了该图标的 MIME 类型,因此浏览器可以快速读取该图标的类型,并在不支持此类型时将其忽略并采用其他图标。 + +在设计图标方面,您应该遵循与任何 Android 图标相同的最佳做法(请参阅 [Android 图标设计指南](https://developer.android.com/guide/practices/ui_guidelines/icon_design.html))。 + +### 将 HTML 链接到 manifest + +要完成 manifest 的设置,您需要从应用程序主页的 HTML 中引用它: + +```html + +``` + +一旦有了 manifest 声明,支持 A2HS 的浏览器就会知道在哪里查找它。 + +## A2HS 不提供什么? + +请记住,将应用程序添加到主屏幕时,它只会使该应用程序易于访问,而不会将应用程序的资料和数据下载到您的设备上,也不会使该应用程序脱机使用或类似的操作。为了使应用离线运行,你必须使用 [Service Worker API](/zh-CN/docs/Web/API/Service_Worker_API) 来离线存储资源,如果需要,还可以使用 [Web Storage](/zh-CN/docs/Web/API/Web_Storage_API) 或 [IndexedDB](/zh-CN/docs/Web/API/IndexedDB_API) 来存储其数据。 + +在示例应用程序中,我们仅使用了一个 service worker 来存储所有必需的文件。service worker 使用[`index.js`](https://github.com/mdn/pwa-examples/blob/master/a2hs/index.js) 文件中的最终的代码块在网站上注册。然后,我们使用 [Cache API](/zh-CN/docs/Web/API/Cache) 缓存网站的所有资产,并使用 [sw.js](https://github.com/mdn/pwa-examples/blob/master/a2hs/sw.js) 文件中的代码从缓存而不是网络中为它们提供服务。 + +## 桌面上的\_A2HS + +虽然 PWA 最初旨在改善移动操作系统上的用户体验,但也有人推进在桌面端安装 PWA 的进程。 + +> **注意:** 在撰写本文时,仅在较新版本的 Chrome 支持以下功能(在 Windows 上默认支持,在 macOS 则需开启 #enable-desktop-pwas 标志)。 + +### 添加安装按钮 + +为了使 PWA 可在桌面上安装,我们首先在文档中添加了一个按钮,以允许用户进行安装—桌面应用程序不会自动提供此按钮,并且需要通过用户手势来触发安装: + +```html + +``` + +然后,我们给它一些简单的样式: + +```css +.add-button { + position: absolute; + top: 1px; + left: 1px; +} +``` + +### 用于处理安装的\_JavaScript + +在 [`index.js` 文件](https://github.com/mdn/pwa-examples/blob/master/a2hs/index.js)的底部,我们添加了一些 JavaScript 代码来处理安装。首先,我们声明一个 `deferredPrompt` 变量(我们将在后面解释),获得对安装按钮的引用,并初始设置为 `display: none`: + +```js +let deferredPrompt; +const addBtn = document.querySelector('.add-button'); +addBtn.style.display = 'none'; +``` + +我们最初隐藏该按钮是因为 PWA 必须满足 A2HS 标准才会安装。条件满足时,支持的浏览器将触发 `beforeinstallprompt` 事件。然后,我们可以使用以下处理程序来处理安装: + +```js +window.addEventListener('beforeinstallprompt', (e) => { + // 防止 Chrome 67 及更早版本自动显示安装提示 + e.preventDefault(); + // 稍后再触发此事件 + deferredPrompt = e; + // 更新 UI 以提醒用户可以将 App 安装到桌面 + addBtn.style.display = 'block'; + + addBtn.addEventListener('click', (e) => { + // 隐藏显示 A2HS 按钮的界面 + addBtn.style.display = 'none'; + // 显示安装提示 + deferredPrompt.prompt(); + // 等待用户反馈 + deferredPrompt.userChoice.then((choiceResult) => { + if (choiceResult.outcome === 'accepted') { + console.log('User accepted the A2HS prompt'); + } else { + console.log('User dismissed the A2HS prompt'); + } + deferredPrompt = null; + }); + }); +}); +``` + +所以我们在这里: + +- 调用 {{domxref("Event.preventDefault()")}} 以防止 Chrome 67 及更早版本自动调用安装提示(此行为在 Chrome 68 已更改)。 +- 将事件对象存储在 `deferredPrompt` 变量中,以便以后可以用于执行实际安装。 +- 将按钮设置为 `display: block`,以便它出现在 UI 中供用户点击。 +- 设置按钮的 `click` 处理程序。 + +点击处理程序包含以下步骤: + +- 通过 `display: none` 再次隐藏按钮—安装应用程序后将不再需要它。 +- 使用 `beforeinstallprompt` 事件对象(存储在 `deferredPrompt` 中)上可用的 `prompt()` 方法触发显示安装提示。 +- 使用 `userChoice` 属性响应用户与提示的交互,该属性再次在 `beforeinstallprompt` 事件对象上可用。 +- 将 `deferredPrompt` 设置为 null,因为不再需要它。 + +于是,点击按钮后安装提示就会消失。 + +![应用安装提示](https://mdn.mozillademos.org/files/16281/chrome-desktop-a2hs-banner.png) + +如果用户选择安装,则将安装该应用程序(可作为独立的桌面应用程序使用),并且不再显示“安装”按钮(如果已经安装了该应用程序,则将不再触发 `onbeforeinstallprompt` 事件)。当您打开应用程序时,它将显示在其自己的窗口中: + +![安装后的应用界面](a2hs-installed-desktop.png) + +如果用户选择“取消”,则应用程序的状态将返回到单击按钮之前的状态。 + +> **备注:** 本部分的代码主要来自 Pete LaPage 的[应用安装横幅/添加到主屏幕](https://developers.google.com/web/fundamentals/app-install-banners/)。 + +## 其他 + +- [渐进式 Web 应用](/zh-CN/docs/Web/Progressive_web_apps) +- [Service Worker 接口](/zh-CN/docs/Web/API/Service_Worker_API) +- [Web manifest 参考](/zh-CN/docs/Web/Manifest) +- [应用安装横幅](https://developers.google.com/web/fundamentals/app-install-banners/) + +{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}} diff --git a/files/zh-cn/web/progressive_web_apps/index.html b/files/zh-cn/web/progressive_web_apps/index.html deleted file mode 100644 index f9d9a3e2fc2fd0..00000000000000 --- a/files/zh-cn/web/progressive_web_apps/index.html +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: 渐进式 Web 应用(PWA) -slug: Web/Progressive_web_apps -tags: - - Apps - - Modern web apps - - PWA - - Progressive web apps - - web app manifest - - 渐进式增强 - - 渐进式增强的 Web 应用程序 -translation_of: Web/Progressive_web_apps ---- -

PWA(Progressive Web Apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。这组文档和指南告诉您有关 PWA 的所有信息。

- -

PWA 的优势

- -

PWA 是可被发现、易安装、可链接、独立于网络、渐进式、可重用、响应性和安全的。关于这些含义的细节,请参阅 PWA 的优势。关于如何实施 PWA,请参阅下面列出的指南。

- -

核心 PWA 指南

- -

以下指南通过简单的示例和工作原理,展示了实施 PWA 需要做什么。

- -
    -
  1. PWA 介绍
  2. -
  3. PWA 结构
  4. -
  5. 通过 Service workers 让 PWA 离线工作
  6. -
  7. 让 PWA 易于安装
  8. -
  9. 通过通知推送让 PWA 可重用
  10. -
  11. 渐进式加载
  12. -
- -
-
-

技术指南

- - -
- -
-

工具

- - -
-
- -

相关链接

- - - -
{{QuickLinksWithSubpages("/zh-CN/docs/Web/Apps/Progressive/")}}
diff --git a/files/zh-cn/web/progressive_web_apps/index.md b/files/zh-cn/web/progressive_web_apps/index.md new file mode 100644 index 00000000000000..3ae6c5b7e98055 --- /dev/null +++ b/files/zh-cn/web/progressive_web_apps/index.md @@ -0,0 +1,65 @@ +--- +title: 渐进式 Web 应用(PWA) +slug: Web/Progressive_web_apps +tags: + - Apps + - Modern web apps + - PWA + - Progressive web apps + - web app manifest + - 渐进式增强 + - 渐进式增强的 Web 应用程序 +translation_of: Web/Progressive_web_apps +--- +PWA(Progressive Web Apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。这组文档和指南告诉您有关 PWA 的所有信息。 + +## PWA 的优势 + +PWA 是可被发现、易安装、可链接、独立于网络、渐进式、可重用、响应性和安全的。关于这些含义的细节,请参阅 [PWA 的优势](/zh-CN/docs/Web/Apps/Progressive/Advantages)。关于如何实施 PWA,请参阅下面列出的指南。 + +## 核心 PWA 指南 + +以下指南通过简单的示例和工作原理,展示了实施 PWA 需要做什么。 + +1. [PWA 介绍](/zh-CN/docs/Web/Apps/Progressive/Introduction) +2. [PWA 结构](/zh-CN/docs/Web/Apps/Progressive/App_structure) +3. [通过 Service workers 让 PWA 离线工作](/zh-CN/docs/Web/Progressive_web_apps/Offline_Service_workers) +4. [让 PWA 易于安装](/zh-CN/docs/Web/Progressive_web_apps/Installable_PWAs) +5. [通过通知推送让 PWA 可重用](/zh-CN/docs/Web/Progressive_web_apps/Re-engageable_Notifications_Push) +6. [渐进式加载](/zh-CN/docs/Web/Progressive_web_apps/Loading) + +## 技术指南 + +- [用户端存储](/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage) — 关于如何使用 Web 存储、IndexedDB 和 Service workers 的指南。 +- [使用 Service workers](/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers) — 关于 Service Worker API 的深入指南。 +- [使用 IndexedDB](/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB) — 有关 IndexedDB 的知识与细节。 +- [使用 Web 存储 API](/zh-CN/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API) — Web 存储 API 简化了工作。 +- [使用应用 Shell 架构的快速加载的 Web 应用](https://developers.google.com/web/updates/2015/11/app-shell) — 使用 App Shell 编码模式创建快速加载的应用的指南。 +- [使用推送 API](/zh-CN/docs/Web/API/Push_API/Using_the_Push_API) — 了解 Web Push API 背后的知识。 +- [使用通知 API](/zh-CN/docs/Web/API/Notifications_API/Using_the_Notifications_API) — 换言之,Web 通知。 +- [响应式设计的构建模块](/zh-CN/docs/Web/Apps/Modern/Responsive/responsive_design_building_blocks) — 学习响应式设计的基础知识,也是现代应用布局的基本话题。 +- [移动优先](/zh-CN/docs/Web/Apps/Modern/Responsive/Mobile_first) — 在创建响应式应用程序布局时,通常将移动布局创建为默认并在顶部构建更宽的布局。 +- [“添加到主屏幕”指南](/zh-CN/docs/Web/Progressive_web_apps/Add_to_home_screen) — 了解如何利用添加到主屏幕(A2HS)。 + +## 工具 + +- [localForage](https://localforage.github.io/localForage/) — 小而妙的 JavaScript 库,它能使客户端数据储存很简单;默认使用 IndexDB,也能在需要时回滚到 Web SQL/Web 存储。 +- [ServiceWorkerWare](https://github.com/fxos-components/serviceworkerware) — 类似 Express 的简易 Service Worker 开发微框架。 +- [oghliner](https://github.com/mozilla/oghliner) — 不只是模板,还是部署离线 Web 应用到 GitHub Pages 的工具。 +- [sw-precache](https://github.com/GoogleChrome/sw-precache) — 一个用于生成预缓存特定资源的 Service Worker 代码的模块。 +- [workbox](https://github.com/GoogleChrome/workbox) — sw-precache 的精神后继者,支持更高级的缓存策略和简易的预缓存功能。 +- [upup](https://www.talater.com/upup/) — 保证站点对用户永远在线的一段小脚本。 +- [The service worker cookbook](https://serviceworke.rs/) — 一系列关于 Service Worker 和推送的优秀章法,着眼于离线应用,而超乎离线应用。 + +## 相关链接 + +- Google Developers 上的[Progressive web apps](https://developers.google.com/web/progressive-web-apps) +- [Progressive Web Apps: Escaping Tabs Without Losing Our Soul](https://medium.com/@slightlylate/progressive-apps-escaping-tabs-without-losing-our-soul-3b93a8561955#.6czgj0myh),由 Alex Russell 撰写 +- [Progressive Web Apps 清单](https://developers.google.com/web/progressive-web-apps/checklist) +- 来自 Google 的 [Lighthouse Tool](https://developers.google.com/web/tools/lighthouse) +- [Tools for building progressive web apps with Angular](https://github.com/angular/mobile-toolkit) +- [React PWA Guide Kit](https://github.com/codebusking/react-pwa-guide-kit) +- [Offline-capable Pokédex web site](https://www.pokedex.org/) +- [Hacker News readers as Progressive Web Apps](https://hnpwa.com/) + +{{QuickLinksWithSubpages("/zh-CN/docs/Web/Apps/Progressive/")}} diff --git a/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html b/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html deleted file mode 100644 index bb903d2b7da86e..00000000000000 --- a/files/zh-cn/web/progressive_web_apps/installable_pwas/index.html +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: 让 PWA 易于安装 -slug: Web/Progressive_web_apps/Installable_PWAs -tags: - - PWAs - - a2hs - - js13kGames - - 可安装 - - 添加至主屏幕 - - 渐进式应用 -translation_of: Web/Progressive_web_apps/Installable_PWAs ---- -
{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}
- -

在上一篇文章,我们了解了如何通过 Service Workerjs13kPWA 离线工作。我们还可以更进一步,让用户如同本地应用一样在支持的移动浏览器上安装 web 应用。这篇文章讲述如何通过网页清单和添加到主屏特性来做到这一点。

- -

这些技术允许应用从设备主屏直接启动,而不需启动浏览器键入 URL。你的 Web 应用可以作为一等公民和原生应用肩并肩。这样可以使应用更易于访问,你还可以指定应用全屏运行,没有浏览器界面,这样看起来更像一个原生应用。

- -

要求

- -

可安装网站需要满足以下条件:

- - - -

清单文件 (Manifest)

- -

离线访问的关键在于一份网页清单,它通过 JSON 形式列举了网站的所有信息。

- -

它通常位于网页应用的根目录,包含一些有用的信息,比如应用的标题、在移动设备操作系统上显示的代表该应用的不同大小的图标(例如主屏图标)的路径,和用于加载页或启动画面的背景颜色。浏览器需要这些信息来安装 web 应用并使其在主屏上显示。

- -

js13kPWAjs13kpwa.webmanifest 文件包含在 index.html 文件的 {{htmlelement("head")}} 段,如下行所示:

- -
<link rel="manifest" href="js13kpwa.webmanifest">
- -
-

注意:这类清单文件有一些曾经常被使用的扩展名:manifest.webapp 在 Firefox OS 应用清单中很流行,而许多人使用 manifest.json 作为网页清单(因为内容是 JSON 格式的)。但是,.webmanifest 扩展名是在W3C 清单规范中显式指定的,所以这里我们使用这个扩展名。

-
- -

文件的内容是这个样子的:

- -
{
-    "name": "js13kGames Progressive Web App",
-    "short_name": "js13kPWA",
-    "description": "Progressive Web App that lists games submitted to the A-Frame category in the js13kGames 2017 competition.",
-    "icons": [
-        {
-            "src": "icons/icon-32.png",
-            "sizes": "32x32",
-            "type": "image/png"
-        },
-        // ...
-        {
-            "src": "icons/icon-512.png",
-            "sizes": "512x512",
-            "type": "image/png"
-        }
-    ],
-    "start_url": "/pwa-examples/js13kpwa/index.html",
-    "display": "fullscreen",
-    "theme_color": "#B12A34",
-    "background_color": "#B12A34"
-}
- -

大部分字段无需解释,但是我们还是详细解释一下为妙:

- - - -

一份网页清单最少需要 name 和一个图标 (带有 src, sizetype)。最好也要提供 descriptionshort_name、和 start_url。除了上述字段,还有一些其它的字段供您使用,请查看网页应用清单参考获取详细信息。

- -

添加到主屏

- -

"添加到主屏" (或者英语短语 A2HS (Add to Home Screen)) 是移动浏览器实现的一个特性,它利用网页清单中的信息来在设备主屏上显示应用图标和文字。只有应用满足上述必备条件,这个功能才可以正常运作。

- -

当用户使用支持的移动浏览器访问 PWA 时,浏览器会显示一条横幅信息表示可以安装这个应用。

- -

Add to Home screen popup of js13kPWA.

- -

在用户单击这个横幅后,安装横幅信息会显示出来。它是浏览器基于网页清单信息创建的,名字和图标可以在提示中看到。

- -

Install banner of js13kPWA.

- -

如果用户单击安装到主屏按钮,会显示应用图标的样子,让用户确认是否安装这个应用。

- -

Add to Home screen popup of js13kPWA.

- -

确认之后,应用就安装到主屏上了。

- -

- -

之后,用户可以立刻启动并使用应用。注意,在某些浏览器/移动操作系统中,PWA 会在图标右下角显示浏览器图标,告诉用户这是一个网页应用。

- -

启动画面

- -

在一些浏览器中,可以通过清单信息产生一个启动画面,当 PWA 启动时显示。

- -

- -

这个启动画面由给定的图标、主题和背景色生成。

- -

总结

- -

在本文中,我们学习了如何使用网页清单和安装到主屏特性让 PWA 可安装。

- -

为了获得添加到主屏的更多信息,请阅读添加到主屏指南。 浏览器支持当前限于 Android 平台上的 Firefox 58+、Mobile Chrome、 Android Webview 31+ 以及 Android Opera 32+, 但是将来这些限制的影响会逐渐减小。

- -

现在让我们将目光转移到 PWA 的最后一个难点:通过推送通知再次启动(re-engagement)。

- -

{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}}

- -

{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}}

diff --git a/files/zh-cn/web/progressive_web_apps/installable_pwas/index.md b/files/zh-cn/web/progressive_web_apps/installable_pwas/index.md new file mode 100644 index 00000000000000..ac773cae23fa80 --- /dev/null +++ b/files/zh-cn/web/progressive_web_apps/installable_pwas/index.md @@ -0,0 +1,122 @@ +--- +title: 让 PWA 易于安装 +slug: Web/Progressive_web_apps/Installable_PWAs +tags: + - PWAs + - a2hs + - js13kGames + - 可安装 + - 添加至主屏幕 + - 渐进式应用 +translation_of: Web/Progressive_web_apps/Installable_PWAs +--- +{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}} + +在上一篇文章,我们了解了如何通过 [Service Worker](/zh-CN/docs/Web/API/Service_Worker_API) 让 [js13kPWA](https://mdn.github.io/pwa-examples/js13kpwa/) 离线工作。我们还可以更进一步,让用户如同本地应用一样在支持的移动浏览器上安装 web 应用。这篇文章讲述如何通过网页清单和添加到主屏特性来做到这一点。 + +这些技术允许应用从设备主屏直接启动,而不需启动浏览器键入 URL。你的 Web 应用可以作为一等公民和原生应用肩并肩。这样可以使应用更易于访问,你还可以指定应用全屏运行,没有浏览器界面,这样看起来更像一个原生应用。 + +## 要求 + +可安装网站需要满足以下条件: + +- 一份网页清单,填好[正确的字段](/en-US/Apps/Progressive/Add_to_home_screen#Manifest) +- 网站的协议必须是安全的(即使用 HTTPS 协议) +- 一个在设备上代表应用的图标 +- 一个注册好的 Service Worker,可以让应用离线工作(这仅对于安卓设备上的 Chrome 浏览器是必需的) + +### 清单文件 (Manifest) + +离线访问的关键在于一份网页清单,它通过 JSON 形式列举了网站的所有信息。 + +它通常位于网页应用的根目录,包含一些有用的信息,比如应用的标题、在移动设备操作系统上显示的代表该应用的不同大小的图标(例如主屏图标)的路径,和用于加载页或启动画面的背景颜色。浏览器需要这些信息来安装 web 应用并使其在主屏上显示。 + +[js13kPWA](https://mdn.github.io/pwa-examples/js13kpwa/) 的 `js13kpwa.webmanifest` 文件包含在 `index.html` 文件的 {{htmlelement("head")}} 段,如下行所示: + +```html + +``` + +> **备注:** 这类清单文件有一些曾经常被使用的扩展名:`manifest.webapp` 在 Firefox OS 应用清单中很流行,而许多人使用 `manifest.json` 作为网页清单(因为内容是 JSON 格式的)。但是,`.webmanifest` 扩展名是在[W3C 清单规范](https://w3c.github.io/manifest/)中显式指定的,所以这里我们使用这个扩展名。 + +文件的内容是这个样子的: + +```json +{ + "name": "js13kGames Progressive Web App", + "short_name": "js13kPWA", + "description": "Progressive Web App that lists games submitted to the A-Frame category in the js13kGames 2017 competition.", + "icons": [ + { + "src": "icons/icon-32.png", + "sizes": "32x32", + "type": "image/png" + }, + // ... + { + "src": "icons/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "/pwa-examples/js13kpwa/index.html", + "display": "fullscreen", + "theme_color": "#B12A34", + "background_color": "#B12A34" +} +``` + +大部分字段无需解释,但是我们还是详细解释一下为妙: + +- `name`: 网站应用的全名。 +- `short_name`: 显示在主屏上的短名字。 +- `description`: 一两句话解释你的应用的用途。 +- `icons`: 一串图标信息:源 URL,大小和类型。多包含几个图标,这样就能选中一个最适合用户设备的。 +- `start_url`: 启动应用时打开的主页。 +- `display`: 应用的显示方式;可以是 `fullscreen`、`standalone`、`minimal-ui` 或者 `browser`。 +- `theme_color`: UI 主颜色,由操作系统使用。 +- `background_color`: 背景色,用于安装和显示启动画面时。 + +一份网页清单最少需要 `name` 和一个图标 (带有 `src`, `size` 和 `type`)。最好也要提供 `description`、`short_name`、和 `start_url`。除了上述字段,还有一些其它的字段供您使用,请查看[网页应用清单参考](/zh-CN/docs/Web/Manifest)获取详细信息。 + +## 添加到主屏 + +"添加到主屏" (或者英语短语 A2HS (Add to Home Screen)) 是移动浏览器实现的一个特性,它利用网页清单中的信息来在设备主屏上显示应用图标和文字。只有应用满足上述必备条件,这个功能才可以正常运作。 + +当用户使用支持的移动浏览器访问 PWA 时,浏览器会显示一条横幅信息表示可以安装这个应用。 + +![Add to Home screen popup of js13kPWA.](js13kpwa-icon.png) + +在用户单击这个横幅后,安装横幅信息会显示出来。它是浏览器基于网页清单信息创建的,名字和图标可以在提示中看到。 + +![Install banner of js13kPWA.](js13kpwa-banner.png) + +如果用户单击安装到主屏按钮,会显示应用图标的样子,让用户确认是否安装这个应用。 + +![Add to Home screen popup of js13kPWA.](js13kpwa-add.png) + +确认之后,应用就安装到主屏上了。 + +![](js13kpwa-installed.png) + +之后,用户可以立刻启动并使用应用。注意,在某些浏览器/移动操作系统中,PWA 会在图标右下角显示浏览器图标,告诉用户这是一个网页应用。 + +### 启动画面 + +在一些浏览器中,可以通过清单信息产生一个启动画面,当 PWA 启动时显示。 + +![](js13kpwa-splash.png) + +这个启动画面由给定的图标、主题和背景色生成。 + +## 总结 + +在本文中,我们学习了如何使用网页清单和安装到主屏特性让 PWA 可安装。 + +为了获得添加到主屏的更多信息,请阅读[添加到主屏指南](/zh-CN/docs/Web/Apps/Progressive/Add_to_home_screen)。 浏览器支持当前限于 Android 平台上的 Firefox 58+、Mobile Chrome、 Android Webview 31+ 以及 Android Opera 32+, 但是将来这些限制的影响会逐渐减小。 + +现在让我们将目光转移到 PWA 的最后一个难点:通过推送通知再次启动(re-engagement)。 + +{{PreviousMenuNext("Web/Apps/Progressive/Offline_Service_workers", "Web/Apps/Progressive/Re-engageable_Notifications_Push", "Web/Apps/Progressive")}} + +{{QuickLinksWithSubpages("/en-US/docs/Web/Progressive_web_apps/")}} diff --git a/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html b/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html deleted file mode 100644 index 7580f158d7e2c3..00000000000000 --- a/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.html +++ /dev/null @@ -1,421 +0,0 @@ ---- -title: 响应式设计的基础 -slug: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks -translation_of: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks ---- -
-

在本文中,我们将讨论响应式设计的主要基本组成部分,并在必要时提供一些指向更多信息的链接。

-
- -

For Web developers, it is now fairly common to be called upon to create a Web site or app that changes its user interface depending on the browser or device accessing the site to provide an optimized experience. One approach to this is to create different versions of your site/app for different platforms or browsers and serve them appropriately after detecting which browser or platform is looking at your site. But this is increasingly inefficient: browser sniffing is inherently error prone, and maintaining multiple copies of your code can turn out to be a nightmare.

- -

It is usually much better to create a single version of your code which doesn't care about what browser or platform is accessing the site, but instead uses feature tests to find out what code features the browser supports or what the values of certain browser features are, and then adjusts the code appropriately. This tends to be termed responsive design or adaptive design, two related but different approaches. For a discussion on the differences between the two, read Responsive design versus adaptive design.

- -

This is much more reliable, more maintainable, and more future proof. You don't get caught in the situation of having to bring out more new site versions as more new browsers and platforms come out, and adjust code as feature support in existing browsers changes.

- -

There are disadvantages to this approach as well. If the content, layout, and functionality need to change greatly for different devices, it may not be such a good approach. Also, taking an existing site and adding responsiveness to it, to make it mobile/tablet friendly, can be a lot more effort than just creating a separate mobile site or app, especially if it is a sprawling enterprise site. Read more about responsive design advantages and disadvantages.

- -
-

You can also read our discussion on the basics of responsive design, if you need some more background information and basics.

-
- -

Fluid grids

- -

The best place to start is with fluid measurements for our application layout — essentially, this means using a combination of percentages and ems/rems to size your containers and text, not fixed widths such as pixels. This has a lot of advantages in that the layout will adapt to different viewport dimensions. Let's look at an example.

- -

We've written a simple-but-fun prototype for an application called Snapshot, which takes a video stream from your webcam (using {{domxref("navigator.getUserMedia", "getUserMedia()")}}) then allows you to capture stills from that video stream (using HTML5 {{HTMLElement("canvas")}}), and save them to a gallery. You can then view previously-captured images and delete them. Other articles will discuss the functionality in more detail, but here we're interested in the layout.

- -
-

Note: You can find the Snapshot app on Github; check out the code and help improve it. You can also see Snapshot running live. Note that getUserMedia() is an experimental technology, which currently only works in Google Chrome and Firefox desktop. More functionality and a clean up of the styling of Snapshot are planned for a future date.

-
- -

Our desktop layout for Snapshot is three columns, containing the camera viewer, image capture view, and gallery, respectively.

- -

- -

The markup is very simple:

- -
<x-deck selected-index="0">
-  <x-card>
-    …
-  </x-card>
-  <x-card>
-    …
-  </x-card>
-  <x-card>
-    …
-  </x-card>
-</x-deck>
- -
-

Note: These weird x- elements may be unfamiliar; they are part of Brick, Mozilla's UI element library for mobile web apps. We have used Brick to create the mobile layout for Snapshot, which you will read more about below.

-
- -

To get these sitting side-by-side we have used the following rules:

- -
x-card {
-  width: 100%;
-}
-
-x-card:nth-child(1), x-card:nth-child(2) {
-  width: 30%;
-  float: left;
-  padding: 2rem;
-}
-
-x-card:nth-child(3) {
-  width: 40%;
-  float: left;
-  height: 100%;
-  overflow: auto;
-  padding: 2rem;
-}
- -

So we're giving the first two columns a {{cssxref("width")}} of 30%, and the third a width of 40%, floating the columns all left. This way they end up side-by-side, and their proportions remain the same as the browser window size varies. This is just a simple grid example, but you can apply this principle to more complex grid layouts as required.

- -

border-box sizing

- -

The padding does not affect the overall width and height of the containers because we have set the {{cssxref("box-sizing")}} of all elements to border-box:

- -
*, *:before, *:after {
-  -webkit-box-sizing: border-box;
-  -moz-box-sizing: border-box;
-  box-sizing: border-box;
-}
- -

This basically means that {{cssxref("width")}} and {{cssxref("height")}} will now set the dimensions of an element all the way up to and including the border, not just the content. So if you set width: 40%, the box width will always be 40% of its parent, and any {{cssxref("padding")}} and {{cssxref("border")}} widths set on the box will be subtracted from the content width, not added to it. Very useful! Read more about this at * { Box-sizing: Border-box } FTW, by Paul Irish.

- -

Flexible replaced elements

- -

Things are working fairly well now, but there are still some issues just waiting to present themselves. For a start, let's have a look at what happens when we include the {{HTMLElement("video")}} and {{HTMLElement("img")}} elements inside our first two columns, naked and unstyled.

- -

- -

Because the size of replaced elements is dictated by the size of the media inserted into them, and the media is a fixed size, they explode out of their containing elements and make a mess of the layout. This is pretty horrible, but generally this kind of problem is easily fixed with some simple CSS:

- -
img, video {
-  max-width: 100%;
-}
- -

This tells the replaced elements to remain constrained inside their container's widths, no matter what. However, if they aren't as wide as their containers, they will not stretch to fill them. In the snapshot example, we ended up with slightly different code:

- -
x-card:nth-child(1) video, x-card:nth-child(2) img {
-  width: 100%;
-    …
-}
- -

This is because in our case, we do in fact want the video and image to stretch to always fill their containers no matter what — a subtle but important difference from {{cssxref("max-width")}} — and therefore always be the same size. The video always resizes dynamically, but the screen captures taken from it do not, so upon resizing the screen it was possible to end up with a messy layout with different sized elements when using max-width: 100%, such as:

- -

- -

Media queries

- -

Fluid grids are a great start, but you'll notice that at certain points (known as breakpoints) the layout starts to break down. At these points you'll want to change the layout to rectify the layout problem, and this can be done using media queries.

- -
-

Note: Media queries are a CSS3 feature that allow you to selectively apply CSS depending on the results of media feature tests — for more on the basics, read Media queries.

-
- -

Typical desktop layout

- -

In our example, we have a desktop layout, as we've already seen. This is created using the CSS rules included at the top of the stylesheet, before any media queries are encountered.
-
-

- -

Mid-width layout

- -

We also have a mid-width layout, which is aimed at working well on tablets and narrow laptop screens. This is created by all of the CSS inside the first media query:

- -
@media all and (max-width: 1024px) {
-  x-card:nth-child(1), x-card:nth-child(2) {
-    width: 50%;
-  }
-
-  x-card:nth-child(3) {
-    width: 100%;
-    clear: left;
-  }
-
-  x-card:nth-child(3) img {
-    width: 20%;
-  }
-}
- -

So here we're altering the widths of the columns and removing the float of the third column (and adding clearing to guard against any float funny business). We've also altered the width of the images inside the third container (no longer a column — this is the gallery) so that now you get five per line (it was previously three per line).

- -

- -

Narrow screen/mobile layout

- -

We then have a narrow screen layout, designed to fit the bill for a mobile app/open Web app experience. This is created in multiple parts. First of all, as expected, there is a media query in our main CSS, which is quite weighty, so we'll go through it in parts:

- -
@media all and (max-width: 480px) {
-  x-card:nth-child(1), x-card:nth-child(2), x-card:nth-child(3) {
-    width: 100%;
-    float: none;
-    padding: 0;
-  }
-
-  button {
-    margin-top: 0;
-    border-radius: 0;
-  }
-
-  x-card:nth-child(1) video, x-card:nth-child(2) img {
-    border-radius: 0px;
-    border: none;
-    padding: 0;
-    background-color: 0;
-  }
- -

This first block resets a number of different things from the widescreen layouts that were't required for the mobile app.

- -
  x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
-    margin-top: 17.5vw;
-  }
-
-  x-card:nth-child(1) button, x-card:nth-child(2) button {
-    position: absolute;
-    bottom: 0;
-  }
-
-  x-card:nth-child(2) button:nth-of-type(2) {
-    bottom: 5.9rem;
-  }
-
-  x-card:nth-child(1) button {
-    font-size: 7vw;
-  }
-
-  x-card:nth-child(2) button {
-    font-size: 7vw;
-  }
- -

The next rules do some sizing on the buttons inside the first two cards, and give all card contents a top margin so that their content won't be lost under the navigation buttons (see below). This was necessary because Mozilla Brick (also see below) forces its components to be 100% of the screen width and height. We have used vw (viewport width) units for these — 1vw is equivalent to 1% of the viewport width. This makes the dimensions scale up and down nicely along with the viewport width. Last for this section, we absolutely positioned all buttons at the bottom of the cards they are in, so the layout looks OK at different viewport size variations. We then add a rule that positions the second button in any card a button's width higher up the card. When you click on an image in the gallery it brings up options to delete or cancel deletion of the card, and you don't want two buttons on top of one another.

- -
x-card:nth-child(3) img {
-  width: 50%;
-}
- -

This rule simply changes the width of the gallery images so now there are two per line.

- -
  nav {
-    width: 100%;
-    position: absolute;
-    z-index: 1000;
-
-    display: -webkit-flex;
-    display: -moz-flex;
-    display: -ms-flexbox;
-    display: flex;
-  }
-
-  nav button {
-    font-size: 6.8vw;
-
-    -webkit-flex: 1;
-    -moz-flex: 1;
-    -ms-flex: 1;
-    flex: 1;
-
-    border-left: 1px solid rgba(100,100,100,0.4);
-  }
-
-  nav button:first-child {
-    border-left: 0;
-  }
-}
- -

In this last set of rules, we change the display value of the {{HTMLElement("nav")}} to flex to make it show (it was set to none in the default CSS at the top of the stylesheet, as it wasn't needed for the other views.) We then use absolute positioning and {{cssxref("z-index")}} to make it take up no space in the document flow, and sit on top of the x-cards (this is why we gave the x-cards that top-margin earlier).

- -

Next up, the font-size of the buttons is set to 6.8vw. Why? Because the top-margin of the x-cards was set to 17vw earlier on. All buttons in the app have been set to have a line-height of 2.5, in the default CSS at the top of the stylesheet (check if you don't believe me.) And 6.8 x 2.5 = 17.

- -

Last, we have used flex: 1; to make the buttons always take up the same proportion of space on the line. Let's have a look at the mobile layout, in the below image.

- -

single column layout for mobile app view, with three buttons to navigate between cards, an image viewer, and a Save Picture button at the button.But there are more tricks up our sleeves for this mobile app layout! As mentioned above, we used Mozilla Brick, a collection of ready-rolled mobile UI components, in the making of the mobile app layout. In particular, we used the deck component for the nice transition effect between cards when the buttons are pressed. For more on using Brick, read Mozilla Brick: ready made UI components.

- -

What's more relevant to this article is that we didn't want the Brick CSS and JavaScript files being applied to the markup unless we were looking at the mobile app view. To achieve this, we applied the Brick CSS to the page using a separate {{HTMLElement("link")}} element with a media attribute:

- -
<link href="dist/brick.css" type="text/css" rel="stylesheet" media="all and (max-width: 480px)">
- -

This says that the whole stylesheet will not be linked to the HTML unless the viewport width is 480px or less. Moving on to the JavaScript, {{HTMLElement("script")}} elements don't accept media attributes, so I had to do this a different way. Fortunately there is a JavaScript construct called {{domxref("window.matchMedia()")}}, which can conditionally run JavaScript constructs depending on whether a media query returns true or not. We opened up the brick.js file and wrapped the whole lot in the following:

- -
if (window.matchMedia("(max-width: 480px)").matches) {
-  // The whole of brick.js goes here!
-}
- -

This causes nothing inside the brick.js file to be run unless the viewport width is 480px or less. Problem solved.

- -

Really wide screens

- -

One thing you might notice is that when the viewport gets very wide (such as on a cinema display), the layout stops getting wider, and just centers in the space available. This is pretty simple to achieve. You could use a min-width media query to fix the {{HTMLElement("body")}} width at a certain point:

- -
@media all and (min-width: 1400px) {
-  body {
-    width: 1400px;
-    margin: 0 auto;
-  }
-}
- -

But it's actually easier to just set the following rule instead, and get rid of the media query altogether:

- -
body {
-  max-width: 1400px;
-  margin: 0 auto;
-}
- -

Orientation fail

- -

We also came across some problems with orientation: the mobile-app layout of our example app is designed for portrait orientation, and looks terrible when viewed on a device in landscape orientation. To fix this, we added in a media query that only applies its contents to the markup when device is viewed in landscape orientation:

- -
@media all and (max-width: 480px) and (orientation: landscape) {
-  nav {
-    width: auto;
-
-    -webkit-flex-direction: column;
-    -moz-flex-direction: column;
-    -ms-flex-direction: column;
-    flex-direction: column;
-  }
-
-  nav button {
-    font-size: 6.8vh;
-  }
-
-  nav button {
-    border-left: 0;
-  }
-
-  x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) {
-    margin-top: 0;
-  }
-
-  x-card:nth-child(1) button, x-card:nth-child(2) button {
-    font-size: 2rem;
-  }
-}
- -

This does the following:

- - - -

This results in the following layout:

- -

- -
-

Note: Another solution with respect to orientation might be to just lock the orientation of your app, to portrait or landscape. If you are working on an installed app, you can easily do this with the orientation manifest field. If you want a solution that works across general web apps, you could use the Screen orientation API, and/or provide a message asking the user to rotate their screen if they are using the wrong orientation (for example, if window.innerWidth is larger than window.innerHeight, assume the
- game is landscape mode and show a "please rotate" message.)

-
- -

Viewport

- -

One last problem to mention for our example app is concerned with mobile browsers and media queries. If we viewed my example in a mobile browser in its current state, we wouldn't see our nice mobile layout. Instead, we'd see the below image.

- -

I'm sure you'll agree that this really isn't what we wanted — why is this happening? In short, mobile browsers lie. They don't render web pages at their true viewport width. Instead, they render pages at a higher assumed viewport width (something approaching a laptop screen), and then shrink the result down to fit inside the mobile screen. This is a sensible defensive mechanism — most old school sites that don't have media queries would look terrible when rendered at say, 320px or 480px wide. But this doesn't help us responsible web developers, who have written small screen layouts into our CSS using media queries and want mobile devices to display those!

- -

There is a way to override this mobile rendering behavior — viewport, which is inserted into our HTML pages in the form of a {{HTMLElement("meta")}} tag. In my example, let's add the following into our HTML {{HTMLElement("head")}}:

- -
<meta name="viewport" content="width=480">
- -

This causes our browser to render our mobile app layout properly — width=480 tells the browser "render this markup at 480 pixels wide", hence the media queries kick in appropriately. There are many more options available in the viewport meta tag, which you can read about in Using the viewport meta tag to control layout on mobile browsers.

- -
-

Note: There is a spec called device adaptation, which defines the same functionality but in CSS, using a @viewport at-rule. This is probably a more logical place to put such information, but the spec is not as well supported as the viewport meta tag, therefore you should stick with that for now.

-
- -

Responsive images/video

- -

Another problem that comes up more and more these days is making image/video weight (size in KB) responsive as well as the dimensions of the image on screen. Yes, you want the images to be contained inside the app UI whether you are using it on desktop or mobile, but you should also consider that mobile apps have much smaller viewport dimensions available than desktop apps, so you should try to give mobile devices a smaller image to download. Mobiles in general (more commonly in some parts of the world than others) are on lower bandwidth connections and have less memory available than desktop devices, so yes, those extra kilobytes really do count.

- -

Another challenge is dealing with high resolution screens — raster graphics designed for low resolutions are in danger of appearing tiny when displayed on a high resolution screen, so devices often apply a default zoom factor to rendered pages to avoid this. The trouble with this, then, is that raster images are zoomed in and as a result can start to look pixellated.

- -

CSS background images

- -

For CSS background images this is a fairly easy problem to solve. If you use the mobile first methodology, you will be creating your mobile layout inside your default CSS, before any media queries have been applied. The media queries then supply CSS that is only applied to the markup when the viewport is above a certain width. Let's look at a quick example:

- -
header {
-  height: 300px;
-  width: 100%;
-  background: url(images/small-header.jpg) center;
-}
-
-@media all and (min-width: 480px) {
-  header {
-    background: url(images/large-header.jpg) center;
-  }
-}
- -

This means that mobile browsers only download the mobile background image asset — not the desktop mobile assets — because they fail the media query tests and therefore ignore the media queries. You can also serve a larger graphic to a higher resolution device using a resolution media query, like so:

- -
button {
-  background: url(images/low-res-header.jpg) 1rem center ;
-}
-
-@media only screen and (-webkit-min-device-pixel-ratio: 2),
-       only screen and ( min-resolution: 192dpi),
-       only screen and ( min-resolution: 2dppx) {
-  button {
-    background: url(images/high-res-header.jpg) 1rem center ;
-  } 
-}
- -

This looks rather complicated, but really it's not — we are providing a number of media query options, as at this time different browsers support different resolution media query types and even units. Brett Jankord has a good explanation at Cross Browser Retina/High Resolution Media Queries.

- -

<video>

- -

HTML5 video is fairly well catered for in terms of responsive capabilities. If you wish, you can point to multiple video files via {{HTMLElement("source")}} attributes, each with their own source and MIME type:

- -
<video controls>
-  <source src="videos/720/crystal720.mp4" type="video/mp4">
-  <source src="videos/720/crystal720.webm" type="video/webm">
-</video>
- -

But you can go one step further. You can include media attributes on the <source> element containing media queries — the video loaded in the browser will depend on both the format the browser supports, and the results of the media tests. So for example:

- -
<video controls>
-  <source src="videos/320/crystal320.mp4" type="video/mp4" media="all and (max-width: 480px)">
-  <source src="videos/320/crystal320.webm" type="video/webm" media="all and (max-width: 480px)">
-  <source src="videos/720/crystal720.mp4" type="video/mp4" media="all and (min-width: 481px)">
-  <source src="videos/720/crystal720.webm" type="video/webm" media="all and (min-width: 481px)">
-</video>
- -

This allows your site to serve different video files based on the available space, in order to optimize the user's experience.

- -

<img>

- -

HTML images are a more difficult proposition. There is no mechanism inherent in HTML images for serving different image files dependent on viewport size, and, due to a number of irksome browser behavior realities, solutions are more difficult to hack together than you would imagine. There are currently some standards proposals in the works that would provide this — the W3C responsive images community group discussed this problem for ages and arrived at the <picture> element, which provides a similar markup structure to {{HTMLElement("video")}}, with {{HTMLElement("source")}} alternatives selectable via media query results. Another proposal, srcset, was put forward by Apple and takes a slightly different approach, instead providing a new srcset attribute for {{HTMLElement("img")}} inside which image references are placed along with "hints" that the browser can use to work out which image is most suitable to display given its viewport size, resolution, etc. These are not intended to be mutually exclusive.

- -

This all sounds good. But those solutions are definitely not ready for production yet — both are in a very early stage of standardization, and have no support across browsers. Currently we have to rely on various polyfills and other solutions, none of which are perfect for all situations, so you need to decide which one is right for your particular situation. Some available solutions are as follows:

- -
-
HiSRC
-
A jQuery plugin that allows you to create small, medium, and large versions of an image, and then serves the appropriate one according to the browser's resolution and available network speed.
-
Mobify.js capturing
-
A very clever technique from Mozilla that allows you to capture the source of the page before it's parsed. This way, you can swap out image src values with JavaScript depending on browser features, circumventing browser preloading issues. This is promising, but doesn't work very well across older browsers.
-
Picturefill
-
A JavaScript-based polyfill for <picture>, which works nicely, but it does require a lot of custom markup.
-
Adaptive images
-
A server-side solution, which records the viewport size in a cookie, then resizes images via a combination of PHP and .htaccess to a more appropriate size, if appropriate. This doesn't require markup or scripting, but has a number of limitations.
-
- -

SVG and other vector graphics

- -

For some image requirements (not photographs, but icons and user interface elements are a good fit), a good solution is to use vector graphics. Because vector images are calculated based on mathematical algorithms rather than containing separate data on every pixel in the image, they tend to be smaller in file size, and are infinitely scalable when zoomed or viewed on high resolution devices (at least, in theory). Some ideas follow, which also help to keep the number of HTTP requests down — another key factor in mobile app performance:

- - - -

See also

- - diff --git a/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.md b/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.md new file mode 100644 index 00000000000000..66c2f0eb6d29ac --- /dev/null +++ b/files/zh-cn/web/progressive_web_apps/responsive/responsive_design_building_blocks/index.md @@ -0,0 +1,439 @@ +--- +title: 响应式设计的基础 +slug: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks +translation_of: Web/Progressive_web_apps/Responsive/responsive_design_building_blocks +--- +在本文中,我们将讨论响应式设计的主要基本组成部分,并在必要时提供一些指向更多信息的链接。 + +For Web developers, it is now fairly common to be called upon to create a Web site or app that changes its user interface depending on the browser or device accessing the site to provide an optimized experience. One approach to this is to create different versions of your site/app for different platforms or browsers and serve them appropriately after detecting which browser or platform is looking at your site. But this is increasingly inefficient: browser sniffing is inherently error prone, and maintaining multiple copies of your code can turn out to be a nightmare. + +It is usually much better to create a single version of your code which doesn't care about what browser or platform is accessing the site, but instead uses feature tests to find out what code features the browser supports or what the values of certain browser features are, and then adjusts the code appropriately. This tends to be termed **responsive design** or **adaptive design**, two related but different approaches. For a discussion on the differences between the two, read [Responsive design versus adaptive design](/zh-CN/docs/Web/Apps/app_layout/Responsive_design_versus_adaptive_design). + +This is much more reliable, more maintainable, and more future proof. You don't get caught in the situation of having to bring out more new site versions as more new browsers and platforms come out, and adjust code as feature support in existing browsers changes. + +There are disadvantages to this approach as well. If the content, layout, and functionality need to change greatly for different devices, it may not be such a good approach. Also, taking an existing site and adding responsiveness to it, to make it mobile/tablet friendly, can be a lot more effort than just creating a separate mobile site or app, especially if it is a sprawling enterprise site. Read more about [responsive design advantages and disadvantages](/zh-CN/docs/Web_Development/Mobile/Responsive_design). + +> **备注:** You can also read our discussion on the basics of [responsive design](/zh-CN/docs/Web_Development/Mobile/Responsive_design), if you need some more background information and basics. + +## Fluid grids + +The best place to start is with fluid measurements for our application layout — essentially, this means using a combination of percentages and ems/rems to size your containers and text, not fixed widths such as pixels. This has a lot of advantages in that the layout will adapt to different viewport dimensions. Let's look at an example. + +We've written a simple-but-fun prototype for an application called Snapshot, which takes a video stream from your webcam (using {{domxref("navigator.getUserMedia", "getUserMedia()")}}) then allows you to capture stills from that video stream (using HTML5 {{HTMLElement("canvas")}}), and save them to a gallery. You can then view previously-captured images and delete them. Other articles will discuss the functionality in more detail, but here we're interested in the layout. + +> **备注:** You can find the [Snapshot app on Github](https://github.com/chrisdavidmills/snapshot); check out the code and help improve it. You can also see [Snapshot running live](https://chrisdavidmills.github.io/snapshot/). Note that `getUserMedia()` is an experimental technology, which currently only works in Google Chrome and Firefox desktop. More functionality and a clean up of the styling of Snapshot are planned for a future date. + +Our desktop layout for Snapshot is three columns, containing the camera viewer, image capture view, and gallery, respectively. + +![](desktop-layout.png) + +The markup is very simple: + +```html + + + … + + + … + + + … + + +``` + +> **备注:** These weird x- elements may be unfamiliar; they are part of [Brick](http://mozbrick.github.io/), Mozilla's UI element library for mobile web apps. We have used Brick to create the mobile layout for Snapshot, which you will read more about below. + +To get these sitting side-by-side we have used the following rules: + +```css +x-card { + width: 100%; +} + +x-card:nth-child(1), x-card:nth-child(2) { + width: 30%; + float: left; + padding: 2rem; +} + +x-card:nth-child(3) { + width: 40%; + float: left; + height: 100%; + overflow: auto; + padding: 2rem; +} +``` + +So we're giving the first two columns a {{cssxref("width")}} of `30%`, and the third a `width` of `40%`, floating the columns all left. This way they end up side-by-side, and their proportions remain the same as the browser window size varies. This is just a simple grid example, but you can apply this principle to more complex grid layouts as required. + +### border-box sizing + +The padding does not affect the overall width and height of the containers because we have set the {{cssxref("box-sizing")}} of all elements to `border-box`: + +```css +*, *:before, *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +``` + +This basically means that {{cssxref("width")}} and {{cssxref("height")}} will now set the dimensions of an element all the way up to and including the border, not just the content. So if you set `width: 40%`, the box width will always be `40%` of its parent, and any {{cssxref("padding")}} and {{cssxref("border")}} widths set on the box will be subtracted from the content width, not added to it. Very useful! Read more about this at [\* { Box-sizing: Border-box } FTW](http://www.paulirish.com/2012/box-sizing-border-box-ftw/), by Paul Irish. + +## Flexible replaced elements + +Things are working fairly well now, but there are still some issues just waiting to present themselves. For a start, let's have a look at what happens when we include the {{HTMLElement("video")}} and {{HTMLElement("img")}} elements inside our first two columns, naked and unstyled. + +![](broken-images.png) + +Because the size of replaced elements is dictated by the size of the media inserted into them, and the media is a fixed size, they explode out of their containing elements and make a mess of the layout. This is pretty horrible, but generally this kind of problem is easily fixed with some simple CSS: + +```css +img, video { + max-width: 100%; +} +``` + +This tells the replaced elements to remain constrained inside their container's widths, no matter what. However, if they aren't as wide as their containers, they will not stretch to fill them. In the snapshot example, we ended up with slightly different code: + +```css +x-card:nth-child(1) video, x-card:nth-child(2) img { + width: 100%; + … +} +``` + +This is because in our case, we do in fact want the video and image to stretch to always fill their containers no matter what — a subtle but important difference from {{cssxref("max-width")}} — and therefore always be the same size. The video always resizes dynamically, but the screen captures taken from it do not, so upon resizing the screen it was possible to end up with a messy layout with different sized elements when using `max-width: 100%`, such as: + +![](broken-max-width-layout.png) + +## Media queries + +Fluid grids are a great start, but you'll notice that at certain points (known as breakpoints) the layout starts to break down. At these points you'll want to change the layout to rectify the layout problem, and this can be done using media queries. + +> **备注:** Media queries are a CSS3 feature that allow you to selectively apply CSS depending on the results of media feature tests — for more on the basics, read [Media queries](/zh-CN/docs/Web/Guide/CSS/Media_queries). + +### Typical desktop layout + +In our example, we have a desktop layout, as we've already seen. This is created using the CSS rules included at the top of the stylesheet, before any media queries are encountered. + +![](desktop-layout.png) + +### Mid-width layout + +We also have a mid-width layout, which is aimed at working well on tablets and narrow laptop screens. This is created by all of the CSS inside the first media query: + +```css +@media all and (max-width: 1024px) { + x-card:nth-child(1), x-card:nth-child(2) { + width: 50%; + } + + x-card:nth-child(3) { + width: 100%; + clear: left; + } + + x-card:nth-child(3) img { + width: 20%; + } +} +``` + +So here we're altering the widths of the columns and removing the float of the third column (and adding clearing to guard against any float funny business). We've also altered the width of the images inside the third container (no longer a column — this is the gallery) so that now you get five per line (it was previously three per line). + +![](middle-layout.png) + +### Narrow screen/mobile layout + +We then have a narrow screen layout, designed to fit the bill for a mobile app/open Web app experience. This is created in multiple parts. First of all, as expected, there is a media query in our main CSS, which is quite weighty, so we'll go through it in parts: + +```css +@media all and (max-width: 480px) { + x-card:nth-child(1), x-card:nth-child(2), x-card:nth-child(3) { + width: 100%; + float: none; + padding: 0; + } + + button { + margin-top: 0; + border-radius: 0; + } + + x-card:nth-child(1) video, x-card:nth-child(2) img { + border-radius: 0px; + border: none; + padding: 0; + background-color: 0; + } +``` + +This first block resets a number of different things from the widescreen layouts that were't required for the mobile app. + +```css + x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) { + margin-top: 17.5vw; + } + + x-card:nth-child(1) button, x-card:nth-child(2) button { + position: absolute; + bottom: 0; + } + + x-card:nth-child(2) button:nth-of-type(2) { + bottom: 5.9rem; + } + + x-card:nth-child(1) button { + font-size: 7vw; + } + + x-card:nth-child(2) button { + font-size: 7vw; + } +``` + +The next rules do some sizing on the buttons inside the first two cards, and give all card contents a top margin so that their content won't be lost under the navigation buttons (see below). This was necessary because Mozilla Brick (also see below) forces its components to be 100% of the screen width and height. We have used `vw` (viewport width) units for these — `1vw` is equivalent to 1% of the viewport width. This makes the dimensions scale up and down nicely along with the viewport width. Last for this section, we absolutely positioned all buttons at the bottom of the cards they are in, so the layout looks OK at different viewport size variations. We then add a rule that positions the second button in any card a button's width higher up the card. When you click on an image in the gallery it brings up options to delete or cancel deletion of the card, and you don't want two buttons on top of one another. + +```css +x-card:nth-child(3) img { + width: 50%; +} +``` + +This rule simply changes the width of the gallery images so now there are two per line. + +```css + nav { + width: 100%; + position: absolute; + z-index: 1000; + + display: -webkit-flex; + display: -moz-flex; + display: -ms-flexbox; + display: flex; + } + + nav button { + font-size: 6.8vw; + + -webkit-flex: 1; + -moz-flex: 1; + -ms-flex: 1; + flex: 1; + + border-left: 1px solid rgba(100,100,100,0.4); + } + + nav button:first-child { + border-left: 0; + } +} +``` + +In this last set of rules, we change the display value of the {{HTMLElement("nav")}} to `flex` to make it show (it was set to `none` in the default CSS at the top of the stylesheet, as it wasn't needed for the other views.) We then use absolute positioning and {{cssxref("z-index")}} to make it take up no space in the document flow, and sit on top of the x-cards (this is why we gave the x-cards that top-margin earlier). + +Next up, the `font-size` of the buttons is set to `6.8vw`. Why? Because the top-margin of the x-cards was set to `17vw` earlier on. All buttons in the app have been set to have a `line-height` of 2.5, in the default CSS at the top of the stylesheet (check if you don't believe me.) And 6.8 x 2.5 = 17. + +Last, we have used `flex: 1;` to make the buttons always take up the same proportion of space on the line. Let's have a look at the mobile layout, in the below image. + +![single column layout for mobile app view, with three buttons to navigate between cards, an image viewer, and a Save Picture button at the button.](mobile-layout.png)But there are more tricks up our sleeves for this mobile app layout! As mentioned above, we used [Mozilla Brick](http://mozilla.github.io/brick/), a collection of ready-rolled mobile UI components, in the making of the mobile app layout. In particular, we used the [deck](http://mozilla.github.io/brick/docs.html#deck) component for the nice transition effect between cards when the buttons are pressed. For more on using Brick, read [Mozilla Brick: ready made UI components](/zh-CN/docs/Web/Apps/app_layout/Mozilla_Brick_ready_made_UI_components). + +What's more relevant to this article is that we didn't want the Brick CSS and JavaScript files being applied to the markup unless we were looking at the mobile app view. To achieve this, we applied the Brick CSS to the page using a separate {{HTMLElement("link")}} element with a `media` attribute: + +```html + +``` + +This says that the whole stylesheet will not be linked to the HTML unless the viewport width is 480px or less. Moving on to the JavaScript, {{HTMLElement("script")}} elements don't accept `media` attributes, so I had to do this a different way. Fortunately there is a JavaScript construct called {{domxref("window.matchMedia()")}}, which can conditionally run JavaScript constructs depending on whether a media query returns `true` or not. We opened up the `brick.js` file and wrapped the whole lot in the following: + +```js +if (window.matchMedia("(max-width: 480px)").matches) { + // The whole of brick.js goes here! +} +``` + +This causes nothing inside the `brick.js` file to be run unless the viewport width is 480px or less. Problem solved. + +### Really wide screens + +One thing you might notice is that when the viewport gets very wide (such as on a cinema display), the layout stops getting wider, and just centers in the space available. This is pretty simple to achieve. You could use a `min-width` media query to fix the {{HTMLElement("body")}} width at a certain point: + +```css +@media all and (min-width: 1400px) { + body { + width: 1400px; + margin: 0 auto; + } +} +``` + +But it's actually easier to just set the following rule instead, and get rid of the media query altogether: + +```css +body { + max-width: 1400px; + margin: 0 auto; +} +``` + +### Orientation fail + +We also came across some problems with orientation: the mobile-app layout of our example app is designed for portrait orientation, and looks terrible when viewed on a device in landscape orientation. To fix this, we added in a media query that only applies its contents to the markup when device is viewed in landscape orientation: + +```css +@media all and (max-width: 480px) and (orientation: landscape) { + nav { + width: auto; + + -webkit-flex-direction: column; + -moz-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + } + + nav button { + font-size: 6.8vh; + } + + nav button { + border-left: 0; + } + + x-card:nth-child(1) video, x-card:nth-child(2) img, x-card:nth-child(3) { + margin-top: 0; + } + + x-card:nth-child(1) button, x-card:nth-child(2) button { + font-size: 2rem; + } +} +``` + +This does the following: + +- Adjusts the nav buttons, changing the direction the flexbox is laid out in, and altering the font size and borders so they sit vertically instead of horizontally. +- Removes the top margin from the x-card contents so you don't end up with an unsightly gap at the top of the screen in landscape mode. +- Changes the sizing of the control buttons (e.g. _Take Picture_, _Delete Photo_) so they don't look too big and sit properly on the screen. + +This results in the following layout: + +![](viewport-fail-fixed.png) + +> **备注:** Another solution with respect to orientation might be to just lock the orientation of your app, to portrait or landscape. If you are working on an installed app, you can easily do this with the [orientation manifest field](https://developer.mozilla.org/en-US/Apps/Build/Manifest#orientation). If you want a solution that works across general web apps, you could use the [Screen orientation API](/zh-CN/docs/Web/API/CSS_Object_Model/Managing_screen_orientation#Locking_the_screen_orientation), and/or provide a message asking the user to rotate their screen if they are using the wrong orientation (for example, if `window.innerWidth` is larger than `window.innerHeight`, assume the +> game is landscape mode and show a "please rotate" message.) + +## Viewport + +One last problem to mention for our example app is concerned with mobile browsers and media queries. If we viewed my example in a mobile browser in its current state, we wouldn't see our nice mobile layout. Instead, we'd see the below image. + +![](viewport-fail.png)I'm sure you'll agree that this really isn't what we wanted — why is this happening? In short, mobile browsers lie. They don't render web pages at their true viewport width. Instead, they render pages at a higher assumed viewport width (something approaching a laptop screen), and then shrink the result down to fit inside the mobile screen. This is a sensible defensive mechanism — most old school sites that don't have media queries would look terrible when rendered at say, 320px or 480px wide. But this doesn't help us responsible web developers, who have written small screen layouts into our CSS using media queries and want mobile devices to display those! + +There is a way to override this mobile rendering behavior — viewport, which is inserted into our HTML pages in the form of a {{HTMLElement("meta")}} tag. In my example, let's add the following into our HTML {{HTMLElement("head")}}: + +```plain + +``` + +This causes our browser to render our mobile app layout properly — `width=480` tells the browser _"render this markup at 480 pixels wide"_, hence the media queries kick in appropriately. There are many more options available in the viewport meta tag, which you can read about in [Using the viewport meta tag to control layout on mobile browsers](/zh-CN/docs/Mozilla/Mobile/Viewport_meta_tag). + +> **备注:** There is a spec called [device adaptation](http://dev.w3.org/csswg/css-device-adapt/), which defines the same functionality but in CSS, using a `@viewport` at-rule. This is probably a more logical place to put such information, but the spec is not as well supported as the viewport meta tag, therefore you should stick with that for now. + +## Responsive images/video + +Another problem that comes up more and more these days is making image/video weight (size in KB) responsive as well as the dimensions of the image on screen. Yes, you want the images to be contained inside the app UI whether you are using it on desktop or mobile, but you should also consider that mobile apps have much smaller viewport dimensions available than desktop apps, so you should try to give mobile devices a smaller image to download. Mobiles in general (more commonly in some parts of the world than others) are on lower bandwidth connections and have less memory available than desktop devices, so yes, those extra kilobytes really do count. + +Another challenge is dealing with high resolution screens — raster graphics designed for low resolutions are in danger of appearing tiny when displayed on a high resolution screen, so devices often apply a default zoom factor to rendered pages to avoid this. The trouble with this, then, is that raster images are zoomed in and as a result can start to look pixellated. + +### CSS background images + +For CSS background images this is a fairly easy problem to solve. If you use the [mobile first](/zh-CN/docs/Web/Apps/app_layout/Mobile_first) methodology, you will be creating your mobile layout inside your default CSS, before any media queries have been applied. The media queries then supply CSS that is only applied to the markup when the viewport is **above** a certain width. Let's look at a quick example: + +```css +header { + height: 300px; + width: 100%; + background: url(images/small-header.jpg) center; +} + +@media all and (min-width: 480px) { + header { + background: url(images/large-header.jpg) center; + } +} +``` + +This means that mobile browsers only download the mobile background image asset — not the desktop mobile assets — because they fail the media query tests and therefore ignore the media queries. You can also serve a larger graphic to a higher resolution device using a resolution media query, like so: + +```css +button { + background: url(images/low-res-header.jpg) 1rem center ; +} + +@media only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and ( min-resolution: 192dpi), + only screen and ( min-resolution: 2dppx) { + button { + background: url(images/high-res-header.jpg) 1rem center ; + } +} +``` + +This looks rather complicated, but really it's not — we are providing a number of media query options, as at this time different browsers support different resolution media query types and even units. Brett Jankord has a good explanation at [Cross Browser Retina/High Resolution Media Queries](http://www.brettjankord.com/2012/11/28/cross-browser-retina-high-resolution-media-queries/). + +### \