banner
genkaim

genkaim

Darkmode,yes \q(≧▽≦q) 主题来自@birdgg

velaOS快应用快速入门

cover
查看原文
最近摸索了一下小米 vela 快应用的编写,写个教程顺便做个归纳 蹭个 hyperOS 的热度。

本文章使用初学者 毕竟我也是。

概述#

在介绍之前,需要了解一个 vela app 基本的架构。

项目结构#

基本架构:

├── manifest.json
├── app.ux
├── pages
│   ├── index
|   |   ├── index.ux
|   |   ├── index.css
|   |   └── index.js
│   └── detail
|       ├── detail.ux
|       ├── detail.css
|       └── detail.js
├── i18n
|   ├── defaults.json
|   ├── zh-CN.json
|   └── en-US.json
└── common
    ├── style.css
    ├── utils.js
    └── logo.png

其中,

  1. manifest.json记录 app 的基本信息

    例如以下属性依次是:

    package: 包名,自己命名。

    name: app 名称,会在手表上显示。

    versionName: 版本名称,自己命名。

    minPlatformVersion: 最低 api 版本。

    icon: 图标文件路径

    {% hideToggle 资源和文件访问规则 %}

    资源和文件访问规则

    应用资源路径分为绝对路径和相对路径,以 "/" 开头的路径表示绝对路径,比如 /common/a.png,不以 "/" 开头的路径是相对路径,比如 a.png 和 ../common/a.png 等。

    应用资源文件分为代码文件和资源文件,代码文件是指 .js/.css/.ux 等包含代码的文件,其他文件则是资源文件,这类文件一般只当作数据来使用,比如图片、视频等。

    1. 在代码文件中,导入其他代码文件时,使用相对路径,比如:../common/component.ux;
    2. 在代码文件中,引用资源文件 (如:图片、视频等) 时,一般情况下使用相对路径,比如: ./abc.png;
    3. 当代码文件需要被导入时,如果导入文件与被导入文件在同一个目录,被导入文件引用资源文件时可以使用相对路径,但如果不在同一目录,必须使用绝对路径,因为被导入文件编译时会被复制到导入文件中,编译后目录会发生变化。比如 a.css 文件被 b.ux 导入,如果 a.css 与 b.ux 在同一个目录,a.css 引用资源文件时可以写相对路径:abc.png,如果不在同一个目录,必须写绝对路径:/common/abc.png,再比如当 a.ux 文件被 b.ux 文件导入时,如果 a.ux 与 b.ux 在同一个目录,a.ux 引用资源文件时可以写相对路径:a.png,如果不在同一个目录,a.ux 引用资源必须写绝对路径:/common/abc.png;
    4. 在 CSS 中,与前端开发一致,使用 url (PATH) 的方式访问资源文件,如:url (/common/abc.png)。

    {% endhideToggle %}

    features: 调用的接口声明,有些敏感接口只有声明才可使用。

    designWidth: 设计稿的尺寸,具体看页面样式与布局

    router: app 中每个页面的定义。

    {
      "package": "com.genkaim.muyu",
      "name": "电子木鱼",
      "versionName": "1.1.0",
      "versionCode": 4,
      "minPlatformVersion": 1000,
      "icon": "/common/logo.png",
      "deviceTypeList": [
        "watch"
      ],
      "features": [
        { "name": "system.storage" },
        { "name": "system.file" },
        { "name": "system.prompt" },
        { "name": "system.vibrator" }
      ],
      "config": {
        "logLevel": "log",
        "designWidth": 600 
      },
      "router": {
        "entry": "pages/index",
        "pages": {
          "pages/index": {
            "component": "index"
          },
          "pages/settings": {
            "component": "settings"
          },
          "pages/confirm": {
            "component": "confirm"
          },
          "pages/about": {
            "component": "about"
          }
        }
      }
    }
    
  2. app.ux为 app 基本的 js 语法例如生命周期中,app 的 onCreate、onDestroy 在这调用

    在下面的代码中,会在 app 打开时和退出时分别 log “onCreate” 和 "onDestroy"。

    <script>
    export default {
      data: {
          a: 1
      },
      onCreate() { 
    	console.log("onCreate")
      },
      onDestroy() {
       	console.log("onDestroy")
      }
    }
    </script>
    
    
  3. pages下每个文件夹为当前页面的资源文件,包含.ux等等。

  4. i18n语言文件。

  5. common存放公用资源文件,比如 logo。

生命周期#

这里借用一下官方的图。

简单来说,app 生命周期就是当打开 app 时,一次进行下面步骤,页面 onShow 前,先经历 onInit 和 onReady 两个阶段(这在 export default 中是具体的两个函数)。

当 app 退出时,为 onDestroy。

生命周期

ux文件的基本认识#

下面是一个简单的.ux文件的内容:

<template>
  <div class="page">
    <text class="title">确定要清除所有功德吗</text>
    <input class="yes" type="button" value="确认" onclick="clearData" />
    <input class="no" type="button" value="取消" onclick="goBack" />
  </div>
</template>

<script>
  import prompt from '@system.prompt'
  export default {
    goBack(event) {
      router.back()
    },
    clearData(event) {
      router.back()
    }
  }
</script>

<style>
.yes, .no {
  width: 500px;
  height: 150px;
  color: white;
  font-size: 40px;
  font-weight: bold;
  margin-top: 60px;
}
.page {
  position: absolute;
  background-color: black;
  display: flex;
  flex-direction: column;
  display: flex;
  justify-content: center; /* 水平居中 */
  align-items: center; /* 垂直居中 */
}
.back {
  position: absolute;
  left: 0px;
  top: 10px;
  background-color: black;
  color: white;
  font-size: 80px;
  font-weight: bold;
  background-image: url('/common/icon-back.png');
}
.yes {
  margin-top: 50px;
  background-color: rgb(44, 44, 44);
}
.no {
  margin-top: 30px;
}
.title {
  color: white;
  font-size: 50px;
  font-weight: bold;
  top: 5px;
}
</style>

  1. template 标签 类似于 HTML 的语言,vela 提供多种组件。例如这里出现两种不同的组件:

​ text 和 input,分别会被渲染为文本块和按钮。

其中:

​ 1.1 class 为自己定义的类名 type 为组件类型,这里为按钮。

​ 1.2 button 标签的 value 值为按钮上显示的文字。

​ 1.3 onclick 为事件绑定,意思为满足” 点按 “这一条件时,会调用指定函数,函数在 export default 定义。

为什么要在 template 下再包一层 div 呢?

因为 template 下只允许存在一个根节点,必须先创建一个节点,内部再放并列节点。

  1. script 脚本 支持 ES5 / ES6 语法,这里的 export default 中,引用了一个模块来实现返回的功能。
  2. **style 层叠样式表:** 用选择器选择并设置属性即可。

简单示例:电子木鱼基本功能实现#

基本功能:#

  1. 页面元素:电子木鱼(图片),显示功德数值功能,” 功德 + 1“动画。

  2. 实现点按木鱼计数功能。

muyu

页面资源:单击下载

页面布局#

  1. 放置木鱼图片可以用 img 组件或者 css 实现,这里使用的前者。

​ 因为需要实现点按反馈的功能,所以需要绑定 onclick 事件。

      <img class="muyu" src="muyu.png" onclick="rpPlusPlus()" />
  1. 计数功能可以通过 text 组件显示,这里注意,动态的数据(变量)可以通过大括号的方式显示,例如下面的代码可以实现显示功德计数(变量在下文)。
      <text class="cnt" >功德 {{localCnt}}</text>
  1. 而动画的实现则是通过 text 组件 +if 条件渲染+ 绑定动画(下文)实现的。

if 条件渲染指 if 内表达式的值为 true 时,会渲染当前元素。

    <text if="{{plus}}" class="showPlus">功德+1</text>

脚本#

  1. 数据处理:

​ 在上文中,需要几个变量:localCnt (int) 用于计数功德,plus (bool) 用于指示消息的显示。

​ 定义变量需要这么写:

​ 其中,

属性类型描述
publicObject页面级组件的数据模型,影响传入数据的覆盖机制:public 内定义的属性允许被传入的数据覆盖,如果外部传入数据的某个属性未被声明,在 public 中不会新增这个属性
protectedObject页面级组件的数据模型,影响传入数据的覆盖机制:protected 内定义的属性,允许被应用内部页面请求传递的数据覆盖,不允许被应用外部请求传递的数据覆盖
privateObject页面级组件的数据模型,影响传入数据的覆盖机制:private 内定义的属性不允许被覆盖
  <script>
  export default {
    public: {
      localCnt: 0,
      plus: false
    }
  }
  </script>
  1. 在上文中,需要定义 rpPlusPlus 函数,实现功德 + 1

​ 其中:

​ 通过 this.Name 访问当前页面数据对象。

​ 并设置 plus 为 true,在 400ms 后设置为 false,即让其显示 400ms,因为它绑定了动画(下文),看 起来的效果就是文字向上移动然后消失。

  rpPlusPlus(event) {
      this.plus = true;
      this.localCnt++;
      setTimeout(() => {
          this.plus = false;
      }, 400);
    }

css#

  1. 绑定动画:

​ 上文已经定义了 class 为 showPlus,所以通过.showPlus选择元素:

 .showPlus {
    position: absolute;/*定位*/
    right: 70px;
    top: 210px;
    color: white;/*字体颜色*/
    font-weight: bold;/*字体粗细*/
    animation-name: moveUp;/*字体绑定动画*/
    animation-delay: 0s;/*动画延迟时间*/
    animation-duration: 200ms;/*动画持续时间*/
    animation-iteration-count: 1;/*动画持续次数*/
  }
  @keyframes moveUp {/*定义动画*/
    0% {
      transform: translateY(0);
    }
    99% {
      transform: translateY(-40px);
    }
    100% {
      visibility: hidden;
    }
  }

  1. cnt、page 和 muyu 元素:
 .muyu {
    position: absolute;/*定位*/
    height: 350px;
    top: 250px;
    background-color: transparent;/*背景颜色*/
    width: 450px;/*大小*/
  }
  .cnt {
    position: absolute;
    bottom: 100px;/*定位*/
    font-size: 70px;/*字体大小*/
    color: white;/*字体颜色*/
  }
   .page {
    background-color: black;/*背景颜色*/
    display: flex;
    flex-direction: column;
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    height: 100%; /* 确保容器占满整个视窗 */
  }

完整代码:

<template>
  <div class="page">
    <img class="muyu" src="muyu.png" onclick="rpPlusPlus()" />
    <text if="{{plus}}" class="showPlus">功德+1</text>
    <text class="cnt" >功德 {{localCnt}}</text>
  </div>
</template>

<script>
  export default {
    public: {
      localCnt: 0,
      plus: false
    },
  rpPlusPlus(event) {
      this.plus = true;
      this.localCnt++;
      setTimeout(() => {
          this.plus = false;
      }, 400);
    }
  }
</script>

<style>
   .page {
    background-color: black;/*背景颜色*/
    display: flex;
    flex-direction: column;
    justify-content: center; /* 水平居中 */
    align-items: center; /* 垂直居中 */
    height: 100%; /* 确保容器占满整个视窗 */
  }
 .showPlus {
    position: absolute;/*定位*/
    right: 70px;
    top: 210px;
    color: white;/*字体颜色*/
    font-weight: bold;/*字体粗细*/
    animation-name: moveUp;/*字体绑定动画*/
    animation-delay: 0s;/*动画延迟时间*/
    animation-duration: 200ms;/*动画持续时间*/
    animation-iteration-count: 1;/*动画持续次数*/
  }
  @keyframes moveUp {/*定义动画*/
    0% {
      transform: translateY(0);
    }
    99% {
      transform: translateY(-40px);
    }
    100% {
      visibility: hidden;
    }
  }
  .muyu {
    position: absolute;/*定位*/
    height: 350px;
    top: 250px;
    background-color: transparent;/*背景颜色*/
    width: 450px;/*大小*/
  }
  .cnt {
    font-size: 70px;/*字体大小*/
    bottom: 100px;/*定位*/
    position: absolute;
    color: white;
  }
</style>

如有错误,欢迎指出。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。