现在您已经了解了 Fuse 的基本概念,是时候将其付诸实践并构建应用程序了。在本教程中,您将学习如何使用 Fuse 框架开发应用程序。具体来说,您将学习以下内容:
如何使用 UX 标记进行编码。
如何使用 Observable、Timer 和 Geolocation api。
如何使用桌面预览和自定义预览来预览应用程序。
如果您需要复习 Fuse,请查看我在本系列中的上一篇文章:跨平台应用程序开发的 Fuse 简介。
先决条件
要开始使用 Fuse,请转到 下载页面并注册一个帐户。如果您有一个现有帐户,您也可以登录。
Fuse 适用于 Windows 和mac OS。为您的平台下载并安装正确的安装程序。在下载页面上,他们还指出了可用于各种文本编辑器的 Fuse 插件。为您的文本编辑器安装一个。Fuse 插件包括代码完成、goto 定义和查看应用程序生成的日志,所有这些都使开发应用程序更加方便。
我们还将介绍如何使用自定义预览来预览应用程序。这需要在您的计算机上安装s android Studio 或 Xcode。
对html、css和javascript等 Web 技术有基本的了解是有帮助的,但不是必需的。
你将要创造什么
您将创建一个秒表应用程序,该应用程序还可以测量所覆盖的距离。距离是使用地理定位来测量的。用户还可以创建圈数,每圈的个人距离和时间将显示在屏幕上。
这是应用程序的外观:
您可以在教程 GitHub repo中查看完整的源代码 。
创建一个新的保险丝项目
安装 Fuse Studio 后,您现在应该能够创建一个新的 Fuse 项目。只需打开 Fuse Studio 并单击New Fuse Project按钮。输入项目名称,然后单击Create:
这将在所选目录中创建一个新文件夹。打开该文件夹并打开MainView.ux 文件。默认情况下,它只会有<App> 标记。更新它以包含一个 <Text>,然后保存文件:
<App> <Text fontSize="25">Hello World!</Text> </App>
现在应该使用您指定的文本更新预览:
这是 Fuse 的主要开发工作流程。只需将更改保存到项目目录中的任何文件,它们就会自动反映在桌面预览中。
您还可以在底部面板中查看日志。您可以使用 console.log(), 像在浏览器中一样触发您自己的操作。唯一的区别是你必须要有JSON.stringify() 对象才能看到它们的值,因为console.log()Fuse 中的实现只能输出字符串。
用户体验标记
现在我们已经准备好构建应用程序了。打开主视图。ux文件并删除<Text>之前的元素。这样,我们可以从一张白纸开始:
<App> </App>
包括字体
就像在 HTML 文档中一样,标准是在页面的实际标记之前包含资产(例如字体、样式表和脚本)。因此,在元素内添加以下<App> 内容:
<Font File="assets/fonts/roboto/Roboto-Thin.ttf" ux:Global="Thin" />
这会导入File属性中指定的字体并为其命名Thin。请注意,这不会使其成为整个页面的默认字体。如果要使用此字体,则必须在Thin要应用它的特定文本上使用其名称 ( )。
您可以 从教程 GitHub repo 下载字体。之后,在根项目目录中创建一个assets/fonts/robot 文件夹,并将.ttf 文件放入其中。
如果您想使用其他字体,可以从dafont.com下载。那是我下载这个应用程序字体的地方。
接下来,我们要在应用程序中使用图标。Fuse 并没有真正的内置元素和图标集来允许您这样做。它提供的是一种在您的应用程序中包含现有图标字体的方法。由于图标字体本质上是字体,我们可以使用相同的方法来包含字体:
<Font File="assets/fonts/icons/fa-solid-900.ttf" ux:Global="FontAwesome" />
您可以从GitHub存储库下载图标字体, 也可以直接从fontawesome.com下载。请注意,并非 fontawesome 上的所有图标都是免费的,因此最好在使用之前检查实际的图标页面。如果您在图标旁边看到“专业”标签,那么您不能在不付费的情况下简单地在项目中使用它。
包括JavaScript
接下来,我们需要包含此页面的 javaScript 文件。我们可以使用 <JavaScript> 元素来做到这一点:
<JavaScript File="scripts/MainView.js"/>
不要忘记 在项目目录的根目录下 创建scripts/ MainView.js文件。
创建新组件
为了最大化代码重用,Fuse 允许我们从现有组件创建自定义组件。在下面的代码中,我们使用 a<Panel>创建自定义按钮。把它想象成一个div 充当其他元素的容器的容器。在这种情况下,我们将它用作创建按钮的可重用组件。
保险丝带有许多元素。有用于布局内容的元素,例如<Panel>用于显示用户控件、页面和导航、脚本和数据的元素,以及用于构建 UI 的原语。每个都有自己的一组属性,允许您修改数据、表示和行为。
要创建可重用组件,请将ux:Class 属性添加到您希望用作基础的表示元素。在这种情况下,我们使用 a<Panel>作为基础。然后,您可以添加一些默认样式。这类似于在 CSS 中进行样式设置的方式。Margin在容器外增加空间。在这里,我们只指定了一个值,因此该边距应用于面板的所有侧面。为元素Color添加背景颜色:
<Panel ux:Class="ToggleBtn" Margin="4" Color="#007bff"> </Panel>
在 内部<Panel>,我们要显示按钮文本。我们想把它变成一个可重用的组件,所以我们需要一种方法来传递属性,以供以后使用这个组件时使用。这允许我们仅通过更改属性来获得不同的结果。
在 中<Panel>,使用要传入的值的数据类型作为元素的名称,然后使用 添加属性的名称ux:Property。然后,您可以使用 显示提供给属性的值{ReadProperty PropertyName},其中 PropertyName是您提供给的值ux:Property。这将允许您在使用 组件Text 时提供属性。<ToggleBtn>
<string ux:Property="Text" /><Text Value="{ReadProperty Text}" Color="#fff" FontSize="18" Alignment="Center" Margin="20,15" />
接下来,我们希望在按下按钮时向用户提供某种反馈。我们可以通过触发器和动画师做到这一点。触发器基本上是事件监听器——在这种情况下, <WhilePressed>. 动画师是您想要在触发器处于活动状态时执行的动画或效果。下面的代码将使按钮10%大于其原始大小并更改其颜色。Duration并DurationBack允许您指定动画达到峰值和结束所需的时间。
<WhilePressed> <Scale Factor="1.1" /> <Change this.Color="#636363" Duration="0.03" DurationBack=".03" /> </WhilePressed>
接下来,我们创建<IconBtn>组件。顾名思义,这是一个仅显示图标作为其内容的按钮。这与前一个组件的工作方式相同,尽管我们在这里做了一些新的事情。
首先是ux:Name 财产。这允许我们为特定元素命名,以便我们以后可以引用它。在这种情况下,我们使用它Color在按下按钮时更改其属性。
我们还使用了一个名为 的条件元素<WhileTrue>。这允许我们在 <WhilePressed> for 的值为虚假时禁用触发器is_running 。一旦我们到达 JavaScript 部分,我们将提供这个变量的值。现在,知道这个变量表示计时器当前是否正在运行。
<Panel ux:Class="IconBtn"> <string ux:Property="Text" /> <Text Font="FontAwesome" Color="#333" ux:Name="LapText" FontSize="40" Alignment="Center">{ReadProperty Text}</Text> <WhileTrue Value="{is_running}"> <WhilePressed> <Change LapText.Color="#ccc" Duration="0.09" /> <!-- change text color --> <Rotate Degrees="90" Duration="0.02"/> <!-- rotate the button by 90 degrees --> </WhilePressed> </WhileTrue> </Panel>
主要内容
我们现在可以继续主要内容。首先,我们将所有内容包装在一个<StackPanel>. 顾名思义,这使我们可以垂直或水平“堆叠”它的孩子。默认情况下,它使用垂直方向,因此我们不需要显式指定它:
<StackPanel Margin="0,25,0,0" Padding="20"> </StackPanel>
在上面的代码中,我们为Margin. 与 CSS 不同的是,值分布是左、上、右、下。如果只指定了两个值,则为左右上下。您可以使用 Fuse Studio 中的选择工具来可视化应用的边距。
接下来,我们为页面添加背景图片。这接受您要使用的背景图像的文件路径。A StretchModeofFill使背景自行拉伸以填满整个屏幕:
<ImageFill File="assets/images/seigaiha.png" StretchMode="Fill" />
您可以从教程 GitHub repo 下载我使用的背景图片。或者您可以在Toptal 网站上找到类似的模式 。
接下来,显示应用程序的名称。下面是经过时间的文本字段。这个文本需要经常更新,所以我们需要把它变成一个可以通过 JavaScript 更新的变量。要输出在此页面的 JavaScript 文件中初始化的一些文本,您需要将变量名包含在curl y 大括号中。稍后,您将看到如何从 JavaScript 文件提供此变量的值:
<Text Value="HIIT Stopwatch" Color="#333" FontSize="18" Alignment="Center" Margin="0,0,0,10" /><Text FontSize="65" Font="Thin" TextAlignment="Center" Margin="0,0,0,20">{time_elapsed}</Text>
接下来,我们使用<IconBtn>我们之前创建的组件——与我们在使用字体 ID 的 Web 环境中不同。在 Fuse 中,您必须使用分配给要使用的图标字体的 Unicode。您还需要&#x用作前缀。当这个按钮被按下(调用Clicked)时,addLap()在 JavaScript 文件中声明的函数被执行:
<IconBtn Text="" Clicked="{addLap}" />
在 Font Awesome 中,您可以在其自己的页面上找到图标字体的 unicode 。
添加新圈的按钮正下方是一些文本,表明上面的按钮用于添加新圈:
<Text Value="Lap" Color="#333" FontSize="15" Alignment="Center" Margin="0,5,0,20" />
接下来,显示用于启动和停止计时器的按钮。这也执行了我们稍后将声明的函数:
<ToggleBtn Text="{toggle_btn_text}" Clicked="{toggle}" />
接下来,我们需要输出用户添加的圈数。这包括圈数、跑过的距离和花费的时间。该<Each>元素允许我们遍历对象集合并显示每个对象的各个属性:
<StackPanel Margin="20,40"> <Each Items="{laps}"> <DockPanel Margin="0,0,0,15"> <Text Alignment="Left" FontSize="18" Color="#333" Value="{title}" /> <Text Alignment="Center" FontSize="18" Color="#333" Value="{distance}" /> <Text Alignment="Right" FontSize="18" Color="#333" Value="{time}" /> </DockPanel> </Each> </StackPanel>
在上面的代码中,我们使用<DockPanel>来包装每个项目的内容。这种类型的面板允许我们将其子项“停靠”在可用空间的不同侧面(顶部、左侧、右侧和底部)。默认情况下,这个位置是它的孩子直接在彼此之上。要使它们均匀分布,您需要添加Alignment属性。
JavaScript 代码
现在我们准备好添加 JavaScript 代码了。在 Fuse 中,JavaScript 主要用于业务逻辑并与设备的本机功能一起工作。与 UI 交互的效果、过渡和动画已经由 UX 标记处理。
首先导入我们需要的所有 API。这包括Observable,主要用于在 UI 中分配变量。然后可以使用 JavaScript 更新这些变量。Timer相当于网页版 JavaScript 中的setTimeoutandsetInterval函数。GeoLocation允许我们获取用户的当前位置:
var Observable = require("FuseJS/Observable"); var Timer = require("FuseJS/Timer"); var GeoLocation = require("FuseJS/GeoLocation");
接下来,我们初始化我们将使用的所有可观察值。这些是您之前在 UX 标记中看到的变量。这些变量的值在应用程序的整个生命周期中都会更新,因此我们将它们设为可观察变量。这有效地允许我们在任何这些值发生变化时更新 UI 的内容:
var time_elapsed = Observable(); // the timer text var toggle_btn_text = Observable(); // the text for the button for starting and stopping the timer var is_running = Observable(); // whether the timer is currently running or not var laps = Observable(); // the laps added by the user
之后,我们现在可以设置切换按钮和计时器文本的初始值:
toggle_btn_text.value = 'Start'; // toggle button default text time_elapsed.value = "00:00:00"; // timer default text
这就是你如何改变一个可观察变量的值。由于这些不在任何函数中,因此应在启动应用程序时立即更新 UI。
为每圈设置计时器、单圈时间和位置的初始值:
var time = 0; // timer var lap_time = 0; // time for each lap var locations = []; // location of the user for each lap
该toggle()函数用于启动和停止定时器。当计时器当前停止并且用户点击切换按钮时,这是我们唯一一次重置计时器和圈数的值。这是因为我们希望用户即使在停止计时器后也能看到这些值。
之后,获取用户的当前位置并将其推送到locations阵列上。这允许我们稍后在用户添加一圈时将其与下一个位置进行比较。然后,创建一个每 10 毫秒执行一次的计时器。我们增加整体time和lap_time 每次执行。然后使用以下函数使用格式化的值更新 UI formatTimer():
function toggle(){ if(toggle_btn_text.value == 'Start'){ // the timer is currently stopped (alternatively, use is_running) laps.clear(); // observable values has a clear() method for resetting its value time_elapsed.value = formatTimer(time); is_running.value = true; locations.push(GeoLocation.location); // get initial location timer_id = Timer.create(function() { time += 1; // incremented every 10 milliseconds lap_time += 1; // current lap time time_elapsed.value = formatTimer(time); // update the UI with the formatted time elapsed string }, 10, true); }else{ // next: add code for when the user stops the timer } toggle_btn_text.value = (toggle_btn_text.value == 'Start') ? 'Stop' : 'Start'; }
当用户停止计时器时,我们使用delete()计时器中的方法将其删除。这需要timer_id 创建计时器时返回的 :
Timer.delete(timer_id); // delete the running timer // reset the rest of the values time = 0; lap_time = 0; is_running.value = false;
接下来是格式化定时器的函数。这是通过将毫秒转换为秒和分钟来实现的。我们已经知道这个函数每 10 毫秒执行一次。并且每次执行时都会time递增。1因此,要获得毫秒,我们只需将time乘以10。从那里,我们只需根据每个测量单位的等效值计算秒和分钟:
function formatTimer(time) { function pad(d) { return (d < 10) ? '0' + d.toString() : d.toString(); } var millis = time * 10; var seconds = millis / 1000; mins = Math.floor(seconds / 60); secs = Math.floor(seconds) % 60, hundredths = Math.floor((millis % 1000) / 10); return pad(mins) + ":" + pad(secs) + ":" + pad(hundredths); }
每次用户点击刷新按钮时,addLap()都会执行该功能。laps 这会在observable 之上添加一个新条目:
function addLap() { if(time > 0){ // only execute when the timer is running lap_time_value = formatTimer(lap_time); // format the current lap time lap_time = 0; // reset the lap time var start_loc = locations[laps.length]; // get the previous location var end_loc = GeoLocation.location; // get the current location locations.push(end_loc); // add the current location var distance = getDistanceFromLatLonInMeters(start_loc.latitude, start_loc.longitude, end_loc.latitude, end_loc.longitude); // add the new item on top laps.insertAt(0, { title: ("Lap " + (laps.length + 1)), time: lap_time_value, distance: distance.toString() + " m." }); } }
这是获取以米为单位的距离的功能。这使用Haversine公式:
function getDistanceFromLatLonInMeters(lat1, lon1, lat2, lon2) { function deg2rad(deg) { return deg * (Math.PI/180) } var R = 6371; // radius of the earth in km var dLat = deg2rad(lat2 - lat1); var dLon = deg2rad(lon2 - lon1); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = (R * c) * 1000; // Distance in m return d; }
不要忘记导出所有可观察值:
module.exports = { toggle: toggle, toggle_btn_text: toggle_btn_text, is_running: is_running, time_elapsed: time_elapsed, laps: laps, addLap: addLap }
地理定位包
为了保持轻量级,Fuse 并没有真正包含它默认支持的所有包。对于地理位置和本地通知等内容,您需要告诉 Fuse 在构建应用程序时将它们包括在内。打开 项目目录根目录下的StopWatch.unoprojFuse.GeoLocation并包含在Packages数组下:
"Packages": [ "Fuse", "FuseJS", "Fuse.GeoLocation" // add this ],
这应该指示 Fuse 在构建应用程序以进行自定义预览或生成安装程序时包含 Geolocation 包。
设置自定义预览
在您可以在您的ios设备上运行该应用程序之前,您需要先向该应用程序添加一个捆绑标识符。打开秒表。unoproj文件并在iOS. 这将是应用提交到应用商店时的唯一标识:
"Packages": [ // ... ], "iOS": { "BundleIdentifier": "com.yourname.stopwatch", "PreviewBundleIdentifier": "com.yourname.stopwatch.preview" }
接下来,在 Xcode 上,使用您的 Apple 开发者帐户登录。如果您还没有,您可以访问Apple 开发者网站并创建一个。在您的 iOS 设备上开发和测试应用程序实际上是免费的。但是,如果您不是开发人员计划的一部分,则存在一些限制。
创建帐户后,转到 Xcode 首选项并添加您的 Apple 帐户。然后点击Manage Certificates并为 iOS 开发添加一个新证书。此证书用于确保应用程序来自已知来源。
完成后,您现在应该可以在您的设备上运行该应用程序了。在 Fuse Studio 中单击Preview > Preview on iOS 并等待它启动 Xcode。打开 Xcode 后,选择您的设备并单击播放按钮。这将构建应用程序并将其安装在您的设备上。如果出现构建错误,很可能是预览包标识符不是唯一的
将Bundle Identifier 更改为 唯一的东西应该可以解决问题。签名部分下的错误消失后,再次单击播放按钮以重建应用程序。这应该在您的设备上安装该应用程序。
但是,在您批准之前,您将无法打开该应用程序。您可以在 iOS 设备上执行此操作,方法是转到 “设置” > “通用 ” > “设备管理”并选择与您的 Apple 开发者帐户关联的电子邮件。批准它,这应该会解锁应用程序。
对于 Android,您应该无需任何额外步骤即可预览应用程序。
结论
而已!在本教程中,您学习了使用 Fuse 框架创建应用程序的基础知识。具体来说,您已经创建了一个秒表应用程序。通过创建这个应用程序,您已经学会了如何使用 Fuse 的 UX 标记和 Fuse 的一些 JavaScript API。您还学习了如何在开发应用程序时使用 Fuse Studio 在您的计算机和手机上预览应用程序。
- 包括字体
- 包括JavaScript
- 创建新组件
- 主要内容
- JavaScript 代码