• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用Fuse创建您的第一个应用程序

现在您已经了解了 Fuse 的基本概念,是时候将其付诸实践并构建应用程序了。在本教程中,您将学习如何使用 Fuse 框架开发应用程序。具体来说,您将学习以下内容:

  • 如何使用 UX 标记进行编码。

  • 如何使用 Observable、Timer 和 Geolocation api

  • 如何使用桌面预览和自定义预览来预览应用程序。

如果您需要复习 Fuse,请查看我在本系列中的上一篇文章:跨平台应用程序开发的 Fuse 简介。

先决条件

要开始使用 Fuse,请转到 下载页面并注册一个帐户。如果您有一个现有帐户,您也可以登录。 

Fuse 适用于 Windows 和mac OS。为您的平台下载并安装正确的安装程序。在下载页面上,他们还指出了可用于各种文本编辑器的 Fuse 插件。为您的文本编辑器安装一个。Fuse 插件包括代码完成、goto 定义和查看应用程序生成的日志,所有这些都使开发应用程序更加方便。

我们还将介绍如何使用自定义预览来预览应用程序。这需要在您的计算机上安装s android Studio 或 Xcode。  

htmlcssjavascript等 Web 技术有基本的了解是有帮助的,但不是必需的。

你将要创造什么

您将创建一个秒表应用程序,该应用程序还可以测量所覆盖的距离。距离是使用地理定位来测量的。用户还可以创建圈数,每圈的个人距离和时间将显示在屏幕上。

这是应用程序的外观:

使用Fuse创建您的第一个应用程序  第1张

您可以在教程 GitHub repo中查看完整的源代码 。

创建一个新的保险丝项目

安装 Fuse Studio 后,您现在应该能够创建一个新的 Fuse 项目。只需打开 Fuse Studio 并单击New Fuse Project按钮。输入项目名称,然后单击Create:

使用Fuse创建您的第一个应用程序  第2张

这将在所选目录中创建一个新文件夹。打开该文件夹并打开MainView.ux 文件。默认情况下,它只会有<App> 标记。更新它以包含一个 <Text>,然后保存文件:

<App>
    <Text fontSize="25">Hello World!</Text>
</App>

现在应该使用您指定的文本更新预览:

使用Fuse创建您的第一个应用程序  第3张

这是 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="&#xf2f1;" 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 后,选择您的设备并单击播放按钮。这将构建应用程序并将其安装在您的设备上。如果出现构建错误,很可能是预览包标识符不是唯一的

使用Fuse创建您的第一个应用程序  第4张将Bundle Identifier 更改为 唯一的东西应该可以解决问题。签名部分下的错误消失后,再次单击播放按钮以重建应用程序。这应该在您的设备上安装该应用程序。 

但是,在您批准之前,您将无法打开该应用程序。您可以在 iOS 设备上执行此操作,方法是转到 “设置”  >  “通用 ” >  “设备管理”并选择与您的 Apple 开发者帐户关联的电子邮件。批准它,这应该会解锁应用程序。

对于 Android,您应该无需任何额外步骤即可预览应用程序。

结论

而已!在本教程中,您学习了使用 Fuse 框架创建应用程序的基础知识。具体来说,您已经创建了一个秒表应用程序。通过创建这个应用程序,您已经学会了如何使用 Fuse 的 UX 标记和 Fuse 的一些 JavaScript API。您还学习了如何在开发应用程序时使用 Fuse Studio 在您的计算机和手机上预览应用程序。


文章目录
  • 先决条件
  • 你将要创造什么
  • 创建一个新的保险丝项目
  • 用户体验标记
    • 包括字体
    • 包括JavaScript
    • 创建新组件
    • 主要内容
    • JavaScript 代码
  • 地理定位包
  • 设置自定义预览
  • 结论