原文を表示
最近、小米 vela のクイックアプリの作成を試してみたので、チュートリアルを書いて、hyperOS の熱を借りる。
この記事は初心者向けに書かれています。私もそうですから。
概要#
紹介の前に、vela アプリの基本的な構造を理解する必要があります。
プロジェクト構造#
基本構造:
├── 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
はアプリの基本情報を記録します。例えば、以下の属性はそれぞれ:
package: パッケージ名、自分で命名します。
name: アプリ名、時計に表示されます。
versionName: バージョン名、自分で命名します。
minPlatformVersion: 最低 API バージョン。
icon: アイコンファイルのパス。
{% hideToggle リソースとファイルのアクセスルール %}
リソースとファイルのアクセスルール
アプリのリソースパスは絶対パスと相対パスに分かれます。"/" で始まるパスは絶対パスを示し、例えば /common/a.png、"/" で始まらないパスは相対パスを示します。例えば a.png や ../common/a.png などです。
アプリのリソースファイルはコードファイルとリソースファイルに分かれます。コードファイルは .js/.css/.ux などのコードを含むファイルを指し、その他のファイルはリソースファイルです。この種のファイルは一般的にデータとして使用されます。例えば、画像、動画などです。
- コードファイル内で他のコードファイルをインポートする際は、相対パスを使用します。例えば:../common/component.ux;
- コードファイル内でリソースファイル(画像、動画など)を参照する際は、一般的に相対パスを使用します。例えば: ./abc.png;
- コードファイルがインポートされる必要がある場合、インポートファイルと被インポートファイルが同じディレクトリにある場合、被インポートファイルがリソースファイルを参照する際は相対パスを使用できますが、同じディレクトリにない場合は絶対パスを使用しなければなりません。なぜなら、被インポートファイルはコンパイル時にインポートファイルにコピーされ、コンパイル後のディレクトリが変更されるからです。例えば、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 を使用しなければなりません;
- CSS では、フロントエンド開発と同様に、url (PATH) の形式でリソースファイルにアクセスします。例えば:url (/common/abc.png)。
{% endhideToggle %}
features: 呼び出すインターフェースの宣言であり、一部の敏感なインターフェースは宣言がないと使用できません。
designWidth: デザイン稿のサイズで、具体的にはページスタイルとレイアウトを参照してください。
router: アプリ内の各ページの定義。
{ "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
はアプリの基本的な js 構文であり、ライフサイクル中にアプリの onCreate、onDestroy をここで呼び出します。以下のコードでは、アプリが開かれたときと終了するときにそれぞれ "log"onCreate"と"onDestroy" を記録します。
<script> export default { data: { a: 1 }, onCreate() { console.log("onCreate") }, onDestroy() { console.log("onDestroy") } } </script>
-
pages
下の各フォルダは現在のページのリソースファイルを含み、.ux
などが含まれます。 -
i18n
言語ファイル。 -
common
は共通リソースファイルを格納します。例えば、ロゴなど。
ライフサイクル#
ここでは公式の図を借用します。
簡単に言うと、アプリのライフサイクルはアプリを開いたときに以下の手順を一度行い、ページの onShow の前に onInit と onReady の 2 つの段階を経ます(これは export default 内で具体的な 2 つの関数です)。
アプリが終了するときは 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>
- template タグ: HTML の言語に似ており、vela はさまざまなコンポーネントを提供します。ここでは 2 種類の異なるコンポーネントが表示されます:
text と input は、それぞれテキストブロックとボタンとしてレンダリングされます。
その中で:
1.1 class は自分で定義したクラス名で、type はコンポーネントのタイプ、ここではボタンです。
1.2 button タグの value 値はボタンに表示されるテキストです。
1.3 onclick はイベントのバインディングで、"タップ" という条件が満たされると、指定された関数が呼び出されます。この関数は export default で定義されています。
なぜ template の下に div をもう一層包む必要があるのですか?
template の下には 1 つのルートノードしか存在できないため、最初に 1 つのノードを作成し、その内部に並列ノードを配置する必要があります。
- script スクリプト: ES5 / ES6 構文をサポートしており、ここでの export default 内では、戻る機能を実現するためにモジュールをインポートしています。
- style スタイルシート: セレクタを使用して属性を選択し、設定するだけです。
簡単な例:電子木魚の基本機能の実装#
基本機能:#
-
ページ要素:電子木魚(画像)、功徳数値表示機能、「功徳 + 1」アニメーション。
-
木魚をタップしてカウント機能を実現します。
ページリソース:クリックしてダウンロード
ページレイアウト#
- 木魚の画像を配置するには img コンポーネントまたは css を使用できますが、ここでは前者を使用します。
タップフィードバック機能を実現する必要があるため、onclick イベントをバインドする必要があります。
<img class="muyu" src="muyu.png" onclick="rpPlusPlus()" />
- カウント機能は text コンポーネントを使用して表示できます。ここで注意が必要なのは、動的データ(変数)は大括弧の形式で表示できることです。例えば、以下のコードは功徳カウント(変数は後述)を表示することができます。
<text class="cnt" >功徳 {{localCnt}}</text>
- アニメーションの実現は text コンポーネント +if 条件レンダリング+ アニメーションのバインディング(後述)によって実現されます。
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>
- 上記では、rpPlusPlus 関数を定義し、功徳 + 1 を実現する必要があります。
その中で:
this.Name を使用して現在のページデータオブジェクトにアクセスします。
plus を true に設定し、400ms 後に false に設定します。つまり、400ms 間表示されるようにします。アニメーションがバインドされているため(後述)、見た目の効果は文字が上に移動して消えることです。
rpPlusPlus(event) {
this.plus = true;
this.localCnt++;
setTimeout(() => {
this.plus = false;
}, 400);
}
css#
- アニメーションのバインディング:
上記で 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;
}
}
- 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>
誤りがあれば、指摘してください。