通過Weex 300行代碼開發一款簡易的跑步App

averyleo 7年前發布 | 19K 次閱讀 Weex 移動開發

Weex 正如它的目標: 一套構建高性能、可擴展的原生應用的跨平臺開發方案

Weex 給大家帶來的無疑是客戶端開發效率的提升,我們可以通過一套代碼,實現web,android, iOS的三個平臺上運行。自己最近嘗試了一次借助weex的插件機制,使用 Weex-Amap地圖插件 可以開發 LBS 相關的應用。

首先我們先來看下運行的效果吧:

iOS 版

Android 版

截圖數據僅供參考

它大概具備下面的一些功能;

  • 統計用戶在運動過程中的距離累計,時間計算等。
  • 存儲用戶的運動數據
  • 使用地圖定位和距離計算的API,實現距離統計。
  • 顯示地圖折線,通過對定位的數據地理位置進行折線繪制
  • 統計用戶運動的數據,計算總距離和時間
  • 點擊用戶的歷史記錄,可以查看軌跡

感覺和大家所用到的app功能相差不多了,但實際上我們借助 Weex 和 Weex-Amap 插件可以非常快速的實現這些功能,下面我們來看下具體怎么實現吧。

使用 weex-toolkit 創建項目

首先我們按照官網的教程安裝 weex-toolkit 。如果已經安裝過請忽略。

$ npm install -g weex-toolit

安裝完成后,我們創建一個項目目錄,比如 running-app 。

weex create running-app

大家可能會看到下面的提示,輸入 y 安裝即可。

This command need to install weexpack. Install now? Yes

項目創建完成后,我們需要添加我們的運行平臺比如android或者ios,這里我們添加 android 平臺。

weex platform add android

添加成功后,我們在通過weex的插件機制,安裝 weex-amap 高德的地圖依賴。

weex plugin add weex-amap

安裝完成后,你可以到項目目錄 plugins 里面看下是否有新增的 weex-amap 的項目目錄,如果存在即表示插件安裝成功。你就可以在src目錄下用we或者vue開發應用代碼了

設計原理

[weex-amap]結合了 高德地圖 多個功能,比如定位,地圖縮放,繪制折現,進行點的標記等常用功能。實現一款跑步應用,我們需要解決最核心的問題就是:

統計一個在運動過程的總距離 (s)

當我們能夠獲取到總距離(s)的時候,和運動時間(t) 通過小學物理知識,我們知道:

速度(v) = 總路程(s) / 總時間(t)

在結合一些公式,我們還可以計算出我們的 卡路里(c);

其中 weex-amap 正好可以解決上面最為核心的問題,我們可以通過定位,然后在通過比較兩個連續點之間的距離,進行累加(微分累計),從而獲取總距離。(當然這只是最為簡單的實現原理,做成完整的app還需要更加科學化的算法)

[weex-amap] 其中提供了這么兩個API

  • getUserLocation 用于獲取用戶的當前位置地理位置,用戶會獲取經緯度 [Long, Lat]

  • getLineDistance 用戶獲取傳入的兩個地理位置的直線距離

除了這兩個API,我們還需要用到地圖的一個組件, 就是折線繪制 weex-amap-polyline 。它可以通過path屬性接收到一組地理位置坐標值,從而在地圖上繪制連續的折線。比如:

<weex-amap-polyline stroke-color="#1ba1e2" stroke-width="3" path="{{your_path}}"></weex-amap-polylone>

其中 your_path 指定類似這樣的數據: [[116.487, 40.00003],[113.487, 40.0002]...]

關于更多的如何使用 weex-amap 插件,可以參考這篇文章 以及 官方 Demos

設計頁面功能和邏輯

大家也都用過跑步的APP,常見的界面布局如下:

那么我們頁面的基本結構就已經出來了:

<template>  
  <div class="container">
    <weex-amap id="map2017" geolocation="true" center="{{pos}}" class="map" sdk-key="{{keys}}" zoom="{{zoom}}">

    </weex-amap>
    <div class="map-controller" if="{{status!=4}}">
      <div class="distance-wrap">
      </div>
      <div class="dashboard">
      </div>
      <div class="btn-wrap">
      </div>
    </div>
  </div>
</template>

<scritp>  
<script>  
  module.exports = {
    data: {
      keys: {
        h5:'f4b99dcd51752142ec0f1bdcb9a8ec02',
        ios: 'c551f83e1e5b19af89c74096f1c0f007',
        android: 'db6a973159cb0c2639ad02c617a786ae'
      },
      zoom: 16,
      pos: [116.48635, 40.00079],
      status: 1,
      polylinePath: []

    },

    methods: {

    }
  }
</script>

其中 我們使用了 weex-amap 組件,其中一些屬性:

  • zoom 表示設置的地圖的縮放級別
  • geolocation 添加地圖定位插件沒如果你需要定位功能,必須設置
  • sdk-key 設置地圖的密鑰,這是地圖開發必須申請 (前往 高德地圖申請 )
  • center 設置地圖的中心,需要設置一個數組,傳入地理位置坐標[116.487, 40.00003]第一個數字表示經度,第二個值表示緯度

其中的樣式參考如下,當然你也可以自己實現一個布局:

.container{
  position: relative;
  flex: 1;
  min-height: 600;
  background-color: #eee;
}
.map{
  flex: 1;
  min-height: 600;
}
.map-controller{
  z-index: 10000;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 500;
  background-color: rgba(255,255,255,1);
  border-top-width: 2;
  border-top-color: rgba(0,0,0,.25);
}
.distance-wrap{
  flex: 1;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.dashboard{
  flex: 1;
  flex-direction: row;
}

.btn-wrap{
  flex: 1;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

定義數據模型

我們需要在界面里顯示四組數據:

  • 運動距離
  • 運動時間
  • 運動消耗
  • 運動配速

自己設計的runningData里面包含了下面一些數據:

runningData: {  
  distance: 0,  // 表示運動的累計距離
  miles: 0,    // 表示運動的累計距離,單位是公里用于界面顯示
  path: [],    // 運動坐標數據
  time: '00:00:00',  // 用于界面的運動時間顯示
  seconds: 0,   // 運動的時間,單位:秒
  speed: 0,    // 配速 
  calories: 0,  // 運動的消耗,單位千卡
}

處于計算的方便其中我設計了幾個用于數據格式的轉換和計算,在我的 utils.js 里面。

這個時候我們需要在模板里面添加一些代碼用于顯示這些數據;

<template>  
<div class="distance-wrap">  
  <text class="distance">{{runningData.miles}}</text>
  <text class="unit">公里</text>
</div>  
<div class="dashboard">  
  <div class="dashboard-item">
    <div class="time-wrap">
      <text class="dashboard-title">運動時間</text>
      <text class="number-lg">{{runningData.time}}</text>
    </div>
  </div>
  <div class="dashboard-item">
    <text class="dashboard-title">配速</text>
    <text class="number-lg">{{runningData.speed}}</text>
  </div>
  <div class="dashboard-item">
    <text class="dashboard-title">熱量</text>
    <text class="number-lg">{{runningData.calories}}</text>
  </div>
</div>  
</template>

添加地圖折線polyline

<weex-amap id="map2017" geolocation="true" center="{{pos}}" class="map" sdk-key="{{keys}}" zoom="{{zoom}}">  
  <weex-amap-polyline path="{{polylinePath}}" stroke-opacity="0.9"  stroke-style="solid" stroke-width="8" stroke-color="#1ba1e2"></weex-amap-polyline>
</weex-amap>

添加流程控制

在我們進行跑步的過成功無疑就是這么幾個狀態,我將它定義在了 status.js

module.exports = {  
  RUNNING_READY: 1, // 跑步開始前
  RUNNING_DOING: 2, // 跑步進行中
  RUNNING_PAUSE: 3, // 跑步暫停中
  RUNNING_END: 4  // 跑步結束,
  RUNNING_PREVIEW: 5 // 數據預覽
};

我們通過這幾個狀態來實現對界面的操作,比如開始或者暫停。這個時候我們需要添加一一些用于界面控制的按鈕。

<template>  
  ...
  <div class="btn-wrap">
    <div class="btn-circle btn-green" if="{{status==1}}" onclick="start">
      <image class="btn-icon" src="https://simg.open-open.com/show/ab95f3676281e1d58867ead2a6669ac6.png"></image>
    </div>
    <div class="btn-circle btn-midnight" if="{{status==2 || status == 3}}" onclick="end">
      <image class="btn-icon" src="https://simg.open-open.com/show/bb87dc75d68c143fb19faaed33cd2e3c.png"></image>
    </div>
    <div class="btn-circle btn-green" if="{{status == 3}}" onclick="continue">
      <image class="btn-icon" src="https://simg.open-open.com/show/ab95f3676281e1d58867ead2a6669ac6.png"></image>
    </div>
    <div class="btn-circle btn-red" if="{{status==2}}" onclick="stop">
      <image class="btn-icon" src="https://simg.open-open.com/show/88da7ae89211b57ebab997e9a8b27088.png"></image>
    </div>
  </div>
<template>

實現流程

我們接下來,按照流程來實現我們的程序邏輯:

const status = require('./lib/status');  
...
module.exports = {  
  // ...
  methods() {
    start() {

    },
    stop() {

    },
    continue() {

    },
    end() {

    },
  }
}

start

開始的業務邏輯很簡單,就是更改頁面狀態到運行中,然后執行程序。

start() {  
  this.status = status.RUNNING_DOING;
  this.runningAmapGeolocation();
}

stop

暫停的話,我們需要清除掉頁面的計時器。

stop() {  
  this.status = status.RUNNING_PAUSE;
  clearInterval(this.timeRecorder); // 計算時間
  clearInterval(this.amapRecorder); // 計算定位
}

end

點擊結束按鈕,我們需要清除計時器,然后顯示出累計的數據就行了,當然做的復雜一點,還可以進行數據的存儲等。

end() {  
  clearInterval(this.timeRecorder);
  clearInterval(this.amapRecorder);
  /* 使用存儲
  * storage.getItem('runningData', (res) => {
  * ...  
  * }) 
  */
}

實現地圖定位

在添加完 weex-amap 模塊后,我們就可以實現地圖的定位和距離計算。

// 引入 amap 模塊
const Amap = require('@weex-module/amap');

etUserLocation(callback) {  
  Amap.getUserLocation(this.$el('map2017').ref, callback);
}

其中callback回調中會返回一個對象:

{
  result: 'success' or 'fail', // 接口調用是否成功
  data: {
    position: [Long, Lat] // 返回經緯度
  }
}

實現地圖距離計算

// 我們引入第三發utils文件,用于一些計算

const utils = require('./lib/utils');


calcDistanceAndSpeed() {  
  const len = this.runningData.path.length
  if(len > 1) {
    // 計算兩個點之前的距離
    Amap.getLineDistance(this.runningData.path[len-1], this.runningData.path[len-2], (res) => {
      if(res.result == 'success') {
        console.log(res.data.distance);
        this.runningData.distance += res.data.distance;
      }
      // 將總長度轉化為千米
      this.runningData.miles = utils.mtoKm(this.runningData.distance);
       // 初略的計算卡路里 
      this.runningData.calories = (this.runningData.distance / 1000).toFixed(2);
      // 速度換算
      this.runningData.speed = utils.calcSpeed(this.runningData.distance, this.runningData.seconds);
    });
  }
}

其中 utils.js 的實現可以參考 這里

讓程序自動采集數據

大家寫JS一定都實現過一個倒計時的程序,常用的解決方案就是 setInterval (關于setInterval 時間的執行的問題可以看 這里 ) 。

當點擊開始按鈕后,我們需要設置一個計時器,用戶進行用戶時間的計算:

countDownTime() {  
  this.timeRecorder = setInterval(() => {
    this.runningData.seconds ++;
    // 進行格式轉化 12s => 00:00:12
    this.runningData.time = utils.setTimeFormat(this.runningData.seconds);
  }, 1000);  
},

// 設置定位的計時器
 runningAmapGeolocation() {
  this.setUserLocation((res) => {
    if(res.result == 'success') {
      this.pos = res.data.position;
      this.runningData.path.push(res.data.position);
    }  
  }); 
  this.amapRecorder= setInterval(() => {
    this.setUserLocation((res) => {
      if(res.result == 'success') {
        this.runningData.path.push(res.data.position);  
        this.polylinePath = Array.from(this.runningData.path);
        this.pos = utils.setPosition(this.runningData.path);
        this.calcDistanceAndSpeed();
      }
    });
  }, 10000);
},

透過代碼我們可以看到程序會大約每隔十秒進行一次定位,然后再進行計算和距離累加。

文件源代碼

打包運行

開發完畢后,我們可以運行命令,讓它安裝到我們的測試手機上。

weex run android

PS: 當然如果你要做出一個 科學 的跑步程序,還需要你加入大量測試和數據的糾正,比如我們在使用過程會遇到定位的偏差,斷網, 用戶沒有開啟定位權限等問題,這些都是我們需要考慮和應對的

運行 Github 上項目

項目運行截圖:

如果大家在實現過程中遇到問題可以參考 Github 上這個項目的一些代碼。相對剛剛這個簡單的功能,它完善了存儲和數據預覽,以及倒計時等小細節。

1.首先克隆這個項目(后面會寫如何自己創建這樣的項目). 確保你自己環境安裝了 weex-toolkit

git clone https://github.com/weex-plugins/amap-running-app

2.進入克隆的項目目錄,然后執行 npm install

3.測試你的需要運行的平臺,比如android 或者 ios

weex plaform add android

4.添加插件 weex-amap

weex plugin add weex-amap

這個時候你就可以運行命令看具體運行的效果了:

weex run android

amap-running-app ,也歡迎PR,拍磚。

擴展閱讀

 

項目主頁:http://www.baiduhome.net/lib/view/home/1490749271493

 

 本文由用戶 averyleo 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!