查看原文
最近摸索了一下小米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
其中,
manifest.json
记录app的基本信息
例如以下属性依次是:
package: 包名,自己命名。
name: app名称,会在手表上显示。
versionName: 版本名称,自己命名。
minPlatformVersion: 最低api版本。
icon: 图标文件路径
{% hideToggle 资源和文件访问规则 %}
资源和文件访问规则
应用资源路径分为绝对路径和相对路径,以"/"开头的路径表示绝对路径,比如 /common/a.png,不以"/"开头的路径是相对路径,比如 a.png 和 ../common/a.png 等。
应用资源文件分为代码文件和资源文件,代码文件是指 .js/.css/.ux 等包含代码的文件,其他文件则是资源文件,这类文件一般只当作数据来使用,比如图片、视频等。
{% 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"
}
}
}
}
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>
pages
下每个文件夹为当前页面的资源文件,包含.ux
等等。
i18n
语言文件。
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>
text和input,分别会被渲染为文本块和按钮。
其中:
1.1 class为自己定义的类名type为组件类型,这里为按钮。
1.2 button标签的value值为按钮上显示的文字。
1.3 onclick为事件绑定,意思为满足”点按“这一条件时,会调用指定函数,函数在export default 定义。
为什么要在template下再包一层div呢?
因为template下只允许存在一个根节点,必须先创建一个节点,内部再放并列节点。
页面元素:电子木鱼(图片),显示功德数值功能,”功德+1“动画。
实现点按木鱼计数功能。
页面资源:单击下载
因为需要实现点按反馈的功能,所以需要绑定onclick事件。
<img class="muyu" src="muyu.png" onclick="rpPlusPlus()" />
<text class="cnt" >功德 {{localCnt}}</text>
if条件渲染指if内表达式的值为true时,会渲染当前元素。
<text if="{{plus}}" class="showPlus">功德+1</text>
在上文中,需要几个变量:localCnt(int) 用于计数功德,plus(bool) 用于指示消息的显示。
定义变量需要这么写:
其中,
属性 | 类型 | 描述 |
---|---|---|
public | Object | 页面级组件的数据模型,影响传入数据的覆盖机制:public 内定义的属性允许被传入的数据覆盖,如果外部传入数据的某个属性未被声明,在 public 中不会新增这个属性 |
protected | Object | 页面级组件的数据模型,影响传入数据的覆盖机制:protected 内定义的属性,允许被应用内部页面请求传递的数据覆盖,不允许被应用外部请求传递的数据覆盖 |
private | Object | 页面级组件的数据模型,影响传入数据的覆盖机制:private 内定义的属性不允许被覆盖 |
<script>
export default {
public: {
localCnt: 0,
plus: false
}
}
</script>
其中:
通过this.Name访问当前页面数据对象。
并设置plus为true,在400ms后设置为false,即让其显示400ms,因为它绑定了动画(下文),看 起来的效果就是文字向上移动然后消失。
rpPlusPlus(event) {
this.plus = true;
this.localCnt++;
setTimeout(() => {
this.plus = false;
}, 400);
}
上文已经定义了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;
}
}
.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>
如有错误,欢迎指出。