本文是一个简单的教程,旨在教你一些创建跨平台 Web 应用程序的基本技能。你将构建一个示例学校计划应用程序,它将在许多不同的平台上提供动态的“应用程序式”体验,并能离线工作。它将使用 Apache Cordova 和 Mozilla 的 Brick Web 组件。
应用程序背后的故事,由 Piotr 撰写
我有两个孩子,我总是忘记他们的学校计划,他们也是。当然我可以 将 HTML 复制到 JSFiddle 并将计划加载为 Firefox 应用程序。不幸的是,这将无法离线加载,目前也不能在 iOS 上运行。相反,我想创建一个应用程序,让我们的家人中的每一个人都能使用,无论他们选择使用什么设备。
我们将构建
一个移动应用程序,它将
- 显示学校计划
- 离线工作
- 在多个平台上运行
先决条件知识
- 在开始之前,你应该了解 HTML、CSS 和 JavaScript 的基础知识。
- 请阅读 说明,了解如何在本教程中加载任何阶段。
- 阅读 Cordova 文档 也是一件好事,尽管我们将在下面解释你需要知道的要点。
- 你还可以了解 Mozilla Brick 组件,以了解它们的功能。
准备工作
在构建示例应用程序之前,你需要准备你的环境。
安装 Cordova
我们决定在这个项目中使用 Apache Cordova,因为它目前是将 HTML 应用程序交付到许多不同平台的最佳免费工具。你可以使用 Web 技术构建应用程序,然后让 Cordova 自动将应用程序移植到不同的原生平台。让我们先安装它。
- 首先 安装 NodeJS:Cordova 是一个 NodeJS 包。
- 接下来,使用
npm
包管理器全局安装 Cordovanpm install -g cordova
注意:在 Linux 或 OS X 上,你可能需要具有 root 访问权限。
安装最新版本的 Firefox
如果你已经有一段时间没有更新 Firefox,你应该 安装最新版本 以确保你拥有所有必要的工具。
安装 Brick
Mozilla Brick 是为应用程序开发人员构建的工具。它是一组现成的 Web 组件,使你能够非常快速地构建和使用常见的 UI 组件。
- 要安装 Brick,我们需要使用 Bower 包管理器。再次使用
npm
安装它npm install -g bower
- 你可以使用以下命令为当前项目安装 Brick
bower install mozbrick/brick
但现在不要这样做 - 你需要将它放在你的项目中,而不仅仅是任何地方。
获取一些示例 HTML
现在你应该找到一些示例 HTML 在项目中使用 - 将你自己的孩子的在线学校计划复制到这个目的,或者 使用我们的示例,如果你没有,但仍然想跟着做。现在将你的标记保存到一个安全的地方。
阶段 1:设置基本 HTML 项目
在本教程的这一部分中,我们将设置基本项目,并在纯 HTML 中显示学校计划。如果你想查看代码在本节结束时应该是什么样子,请参阅 GitHub 上的阶段 1 代码。
- 首先设置一个简单的 Cordova 项目。在你的命令行中,转到你要创建应用程序项目的目录,并输入以下命令
cordova create school-plan com.example.schoolplan SchoolPlan
这将创建一个包含一些文件的
school-plan
目录。 - 在
school-plan
内部,在你的文本编辑器中打开www/index.html
,并从<body>
元素内部删除所有内容。 - 将你之前保存的学校计划 HTML 复制到单独的元素中。这可以根据你想要的方式进行结构化,但我们建议使用 HTML
<table>
来保存每个单独的计划</head> <body> <h1>Angelica</h1> <table> <thead> <tr> <th></th> <th>Monday</th> <th>Tuesday</th> <th>Wednesday</th> <th>Thursday</th> <th>Friday</th> </tr> </thead> <tbody> <tr> <td>1.</td> <td>Art</td> <td>English</td> ...
- 如果你愿意,更改
www/css/index.css
中包含的样式,以使表格看起来像你想要的样子。我们选择使用“斑马条纹”以便于阅读。table { width: 100%; border-collapse: collapse; font-size: 10px; } th { font-size: 12px; font-weight: normal; color: #039; padding: 10px 8px; } td { color: #669; padding: 8px; } tbody tr:nth-child(odd) { background: #e8edff; }
- 为了快速轻松地测试应用程序,请添加
firefoxos
平台作为 Cordova 目标,并准备应用程序,输入以下两个命令cordova platform add firefoxos cordova prepare
每次你想检查更改时,都需要执行最后一步。
- 在 Firefox 浏览器中打开 应用程序管理器。按下 [添加打包应用程序] 按钮,然后导航到准备好的 firefoxos 应用程序目录,它应该位于
school-plan/platforms/firefoxos/www
中。注意:如果你运行的是 Firefox Aurora 或 Nightly,你可以使用我们新的 WebIDE 工具 完成这些任务,它具有与应用程序管理器类似但略有不同的工作流程。
- 按下 [启动模拟器] 按钮,然后按下 [更新],你将看到应用程序在 Firefox OS 模拟器中运行。你可以使用应用程序管理器检查、调试和分析它 - 阅读 使用应用程序管理器 以获取更多详细信息。
- 现在让我们将应用程序导出为原生 Android APK,以便我们可以在该平台上看到它运行。添加平台并让 Cordova 使用以下两个命令构建 apk 文件
cordova platform add android cordova build android
- apk 构建在
school-plan/platforms/android/ant-build/SchoolPlan-debug.apk
中 - 阅读 Cordova Android 平台指南 以获取有关如何测试它的更多详细信息。
阶段 2
在我们应用程序实现的阶段 2 中,我们将研究使用 Brick 来改善应用程序的用户体验。我们将实现一个 Brick 自定义元素,使我们能够在同一个地方显示不同的计划,而不是必须滚动浏览许多课程计划才能找到你想要的计划。
你可以在 GitHub 上查看 已完成的阶段 2 代码。
我们将使用 brick-deck
组件。它提供了一个“卡片组”类型的界面,显示一张 brick-card
并隐藏其他卡片
- 首先,在
www
目录中运行以下命令,将所需的整个 Brick 代码库安装到app/bower_components
中。bower install mozbrick/brick
- 为了使用它,将以下代码添加到你的
index.html
文件的<head>
中,以导入它的 HTML 和 JavaScript<script src="app/bower_components/brick/dist/platform/platform.js"></script> <link rel="import" href="app/bower_components/brick-deck/dist/brick-deck.html">
- 接下来,所有计划都需要包裹在一个
<brick-deck>
自定义元素中,每个单独的计划都应该包裹在一个<brick-card>
自定义元素中 - 结构最终应该类似于以下内容<brick-deck id="plan-group" selected-index="0"> <brick-card selected> <table> <!-- school plan 1 --> </table> </brick-card> <brick-card> <table> <!-- school plan 2 --> </table> </brick-card> </brick-deck>
brick-deck
组件要求你将<html>
和<body>
元素的高度设置为 100%。将以下内容添加到css/index.css
文件中html, body {height: 100%}
- 当你运行应用程序时,第一张卡片应该是可见的,而其他卡片则保持隐藏。为了处理这个问题,我们现在将在 JavaScript 中添加一些内容。首先,向
index.html
添加一些<link>
元素,以将必要的 JavaScript 文件链接到 HTML<script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" src="js/index.js"></script>
cordova.js
包含有用的通用 Cordova 特定帮助程序函数,而 index.js 将包含我们应用程序的特定 JavaScript。index.js
已经包含了app
变量的定义。应用程序在调用app.initialize()
后运行。在window
加载后调用它是一个好主意,因此添加以下内容window.onload = function() { app.initialize(); }
- Cordova 添加了一些事件;其中之一 -
deviceready
- 在所有 Cordova 代码加载并初始化后触发。让我们将主要的应用程序操作代码放在此事件的回调 -app.onDeviceReady
中。onDeviceReady: function() { // starts when device is ready },
- Brick 向所有其元素添加了一些函数和属性。在这种情况下,
loop
和nextCard
被添加到<brick-deck>
元素中。由于它包含一个id="plan-group"
属性,从 DOM 获取该元素的适当方法是document.getElementById
。我们希望卡片在touchstart
事件触发时切换;此时,nextCard
将从回调app.nextPlan
中调用。onDeviceReady: function() { app.planGroup = document.getElementById('plan-group'); app.planGroup.loop = true; app.planGroup.addEventListener('touchstart', app.nextPlan); }, nextPlan: function() { app.planGroup.nextCard(); }
阶段 3
在本教程的这一部分中,我们将添加一个菜单栏,其中包含当前显示的计划的名称,以提供额外的可用性增强功能。我们将使用 Brick 的 brick-tabbar
组件。查看 GitHub 上的 已完成的阶段 3 代码。
- 我们需要导入该组件。将以下行添加到你的 HTML 的
<head>
中<script src="app/bower_components/brick/dist/platform/platform.js"></script> <link rel="import" href="app/bower_components/brick-deck/dist/brick-deck.html"> <link rel="import" href="app/bower_components/brick-tabbar/dist/brick-tabbar.html">
- 接下来,向所有卡片添加一个 id,并将它们作为
brick-tabbar-tab
元素上的 target 属性的值包含在内,如下所示<brick-tabbar id="plan-group-menu" selected-index="0"> <brick-tabbar-tab target="angelica">Angelica</brick-tabbar-tab> <brick-tabbar-tab target="andrew">Andrew</brick-tabbar-tab> </brick-tabbar> <brick-deck id="plan-group" selected-index="0"> <brick-card selected id="angelica"> ...
- Deck 的
nextCard
方法由 Brick 在后台使用选项卡的reveal
事件调用。当选项卡栏元素被触摸时,卡片将发生变化。应用程序变得更简单,因为我们现在使用的是内置的 Brick 功能,而不是我们自己的自定义代码和 Cordova 功能。如果你想在这里结束教程,你可以安全地从index.html
文件中删除链接到 index.js 和 cordova.js 的<script>
元素。
阶段 4
为了进一步改善触摸设备上的用户体验,我们现在将添加功能,使你能够左右滑动以在卡片之间导航。查看 GitHub 上的 已完成的阶段 4 代码。
- 目前,卡片切换是使用
tabbar
组件完成的。要使选定的选项卡与当前card
保持同步,你需要将它们链接回来。这是通过监听每个card
的show
事件来完成的。对于存储在app.planGroupMenu.tabs
中的每个选项卡tab.targetElement.addEventListener('show', function() { // select the tab });
- 由于存在竞态条件(
planGroupMenu.tabs
在应用初始化时可能不存在),轮询 用于等待合适的时机,然后再尝试分配事件。function assignTabs() { if (!app.planGroupMenu.tabs) { return window.setTimeout(assignTabs, 100); } // proceed
将标签链接到其关联卡片的代码如下所示
onDeviceReady: function() { app.planGroupMenu = document.getElementById('plan-group-menu'); function assignTabs() { if (!app.planGroupMenu.tabs) { return window.setTimeout(assignTabs, 100); } for (var i=0; i < app.planGroupMenu.tabs.length; i++) { var tab = app.planGroupMenu.tabs[i]; tab.targetElement.tabElement = tab; tab.targetElement.addEventListener('show', function() { this.tabElement.select(); }); } }; assignTabs(); // continue below ...
- 在 Firefox OS 应用中检测单指滑动非常容易。需要两个回调函数来监听
touchstart
和touchend
事件,并计算pageX
参数的增量。不幸的是,如果手指移动,Android 和 iOS 不会触发touchend
事件。显然,监听touchmove
事件是最好的选择,但该事件只触发一次,因为它被scroll
事件拦截。最佳的解决办法是在touchmove
回调函数中调用preventDefault()
来阻止事件冒泡。这样,scroll
就会被禁用,功能可以正常运作。// ... continuation app.planGroup = document.getElementById('plan-group'); var startX = null; var slideThreshold = 100; function touchStart(sX) { startX = sX; } function touchEnd(endX) { var deltaX; if (startX) { deltaX = endX - startX; if (Math.abs(deltaX) > slideThreshold) { startX = null; if (deltaX > 0) { app.previousPlan(); } else { app.nextPlan(); } } } } app.planGroup.addEventListener('touchstart', function(evt) { var touches = evt.changedTouches; if (touches.length === 1) { touchStart(touches[0].pageX); } }); app.planGroup.addEventListener('touchmove', function(evt) { evt.preventDefault(); touchEnd(evt.changedTouches[0].pageX); });
您可以添加任意数量的计划 - 只需确保它们的标题在标签栏中适合屏幕。操作将自动分配。
待续...
我们正在准备下一部分,其中该应用将发展成为一个具有可下载计划的市场应用。敬请期待!
[编辑] 请查看 下一部分,我们在其中将数据与 DOM 分离。
关于 Piotr Zalewa
Piotr Zalewa 是 Mozilla 开发者生态系统团队的高级 Web 开发人员。致力于 Web 应用开发。他是 JSFiddle 的创建者。
关于 Chris Mills
Chris Mills 是 Mozilla 的高级技术作家,他编写关于开放 Web 应用、HTML/CSS/JavaScript、A11y、WebAssembly 等方面的文档和演示。他喜欢使用 Web 技术,并偶尔在会议和大学做技术演讲。他曾为 Opera 和 W3C 工作,喜欢演奏重金属鼓和喝好啤酒。他住在英国曼彻斯特附近,与他的爱人和三个美丽的孩子住在一起。
12 条评论