小程式TodoList實踐

ch563發表於2019-03-03

TodoList實踐

看完官方的文件介紹後,就想找個簡單的例子來驗證實現一下,TodoList MVC就很好了,簡單容易。

之前都用JQ、Backbone、vue簡單擼過,大概功能如下:

  • 新增todo
  • 儲存在應用快取
  • 列表展示
  • 區分狀態顯示:全部、未完成、已完成
  • 改變todo狀態
  • 刪除todo

根據以上功能,小程式完成如下:

GitHub: github.com/CH563/TodoL…

小程式TodoList實踐

下面記錄一下我的完成過程:

下載小程式開發工具:開發者工具下載

安裝完成後,使用微信掃一掃登入,選好資料夾後建立即可,開發工具會自動生成以下目錄:

pages/

app.js

app.json

app.wxss

文錄紹構和具體配置檢視官方文件:mp.weixin.qq.com/debug/wxado…

基本配置

由於平時開發習慣用Less,如果在直接使用小程式的wxss來編寫的話,就恢復原生編寫方式,大大的不便,所以直接使用了gulp來實時編譯Less,和修改檔名為wxss。小程式開發工具是不支援Less,直接用vscode來開發,小程式開發工具是用實時預覽和除錯即可,vscode也有豐富的外掛支待小程式語法提示。

// gulpfile.js
var gulp = require(`gulp`)
var less = require(`gulp-less`)
var plumber = require(`gulp-plumber`)
var rename = require(`gulp-rename`)

gulp.task(`less`, function () {
  return gulp.src(`./app.less`)
    .pipe(plumber())  // 錯誤處理
    .pipe(less()) // 編譯less
    .pipe(rename((path) => path.extname = `.wxss`)) // 編譯後生成檔案修改字尾為.wxss
    .pipe(gulp.dest(`./`));
});
gulp.watch(`./app.less`, [`less`]); // 實時監控app.less檔案變化,執行任務
複製程式碼

UI元件也直接引用了小程式支援的weui-wxss

@import "./weui.wxss";
複製程式碼

在app.json定義好小程式頁面路由和配色:

{
  "pages":[
    "pages/index/index"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#ca2100",
    "navigationBarTitleText": "TodoList",
    "navigationBarTextStyle":"white"
  }
}
複製程式碼

頁面開發

頁面檔案都存放在pages/目錄下,每個功能頁面都會建立一個資料夾,TodoList現只需一個頁面完成即可

資料繫結使用 Mustache 語法(雙大括號)將變數包起來

<text class="userinfo-nickname">{{userInfo.nickName}}</text>
<!-- 三元運算 -->
<text class="{{status === `1`?`active`:``}}" data-status="1" bindtap="showStatus">全部</text>
複製程式碼

新增todo

使用欄位addShow來判斷新增輸入層顯示隱藏即可

input輸出框這裡不是雙向繫結,所以這裡新增一個事件bindinput="setInput"來賦值實時變化

<view class="addForm {{addShow?``:`hide`}}">
    <view class="addForm-div">
      <input class="weui-input" placeholder="請輸入todo" value="{{addText}}" bindinput="setInput" focus="{{focus}}" />
      <view class="addForm-btn">
        <button class="weui-btn mini-btn" type="warn" bindtap="addTodo" size="mini">確定新增</button>
        <button class="weui-btn mini-btn" type="default" bindtap="addTodoHide" size="mini">取消</button>
      </view>
    </view>
  </view>
複製程式碼

實時賦值事件處理

setInput: function (e) {
    this.setData({
      addText: e.detail.value
    })
}
複製程式碼

取消時,需要清空input的值,input裡需要繫結value="{{addText}}"

Page({
 data:{
     //...
 }, 
 //...
 addTodoHide: function () {
    this.setData({
      addShow: false, // 控制新增輸入皮膚隱藏
      focus: false, // 失去焦點
      addText: `` // 清空值
    })
 }
 //...
})
複製程式碼

新增todo

Page({
 data:{
     //...
 }, 
 //...
 addTodo: function () {
    // 檢查有沒有輸入
    if (!this.data.addText.trim()) {
      return
    }
    var temp = this.data.lists // 取出lists
    var addT = {
      id: new Date().getTime(), // 取當前時間
      title: this.data.addText,
      status: `0`
    }
    temp.push(addT) // 新增新的todo
    this.showCur(temp) // 處理當前狀態的方法
    this.addTodoHide() // 新增成功後,隱藏新增皮膚方法
    wx.setStorage({ // 小程式非同步快取
      key:"lists",
      data: temp
    })
    wx.showToast({ // weui toast元件
      title: `新增成功!`,
      icon: `success`,
      duration: 1000
    });
 }
 //...
})
複製程式碼

列表部分

scroll-view內滾動

列表渲染,事件觸發,利用data傳參,bind繫結事件

<scroll-view class="lists" scroll-y>
    <!-- 判斷列表是否為空 -->
    <block wx:if="{{curLists.length < 1}}">
      <view class="nodata">暫無資料</view>
    </block>
    <!-- 列表渲染 -->
    <view class="item" wx:for="{{curLists}}" wx:key="index">
      <!-- 內容view,繫結touch三個件事,來實現滑動冊除 -->
      <view class="content" style="{{item.txtStyle}}" data-index="{{index}}" bindtouchstart="touchS" bindtouchmove="touchM" bindtouchend="touchE">
        <!-- checkbox圖示,changeTodo事件來控制狀態切換 -->
        <icon class="icon-small" type="{{item.status === `0`?`circle`:`success`}}" size="23" data-item="{{item.id}}" bindtap="changeTodo"></icon>
        <text class="title {{item.status === `1`?`over`:``}}">{{item.title}}</text>
        <!-- api.formatTime是使用了wxs模組化編寫的模組 -->
        <text class="time">{{api.formatTime(item.id)}}</text>
      </view>
      <!-- 刪除按鈕,繫結刪除事件 -->
      <view class="del" data-item="{{item.id}}" bindtap="delTodo"><text>刪除</text></view>
    </view>
  </scroll-view>
複製程式碼

滑動刪除

效果:當向左滑動時,content跟隨手指像左移動,同時右側出現del按鈕;當滑動距離大於按鈕寬度一半鬆開手指時自動滑動到左側顯示出按鈕,小於一半時自動回到原來的位置,隱藏按鈕。

實現思路:
content和del按鈕分別是絕對定位,利用z-index層來控制讓content來蓋住del,當content向左滑動時,del按鈕就會露出來。

微信小程式api提供的touch物件和3個有關手指觸控的函式(touchstart,touchmove,touchend)來實現content隨手指移動

小程式TodoList實踐
小程式TodoList實踐

詳細api說明,請檢視:mp.weixin.qq.com/debug/wxado…

列表的content已繫結這個三個事件:bindtouchstart="touchS" bindtouchmove="touchM" bindtouchend="touchE"

實現方法:

注意txtStyle,在content在繫結這個屬性的,實現跟隨手指移動的style="{{item.txtStyle}}"

delBtnWidth為了del按鈕的寬度,這裡以rpx為單位

Page({
 data:{
     //...
 }, 
 //...
  touchS: function (e) {
    // console.log(`開始:` + JSON.stringify(e))
    // 是否只有一個觸控點
    if(e.touches.length === 1){
      this.setData({
        // 觸控起始的X座標
        startX: e.touches[0].clientX
      })
    }
  },
  touchM: function (e) {
    // console.log(`移動:` + JSON.stringify(e))
    var _this = this
    if(e.touches.length === 1){
     // 觸控點的X座標
      var moveX = e.touches[0].clientX
      // 計算手指起始點的X座標與當前觸控點的X座標的差值
      var disX = _this.data.startX - moveX
     // delBtnWidth 為右側按鈕區域的寬度
      var delBtnWidth = _this.data.delBtnWidth
      var txtStyle = ``
      if (disX == 0 || disX < 0){ // 如果移動距離小於等於0,文字層位置不變
        txtStyle = `left:0`
      } else if (disX > 0 ){ // 移動距離大於0,文字層left值等於手指移動距離
        txtStyle = `left:-` + disX + `rpx`
        if(disX >= delBtnWidth){
          // 控制手指移動距離最大值為刪除按鈕的寬度
          txtStyle = `left:-` + delBtnWidth + `rpx`
        }
      }
      // 獲取手指觸控的是哪一個item
      var index = e.currentTarget.dataset.index;
      var list = _this.data.curLists
      // 將拼接好的樣式設定到當前item中
      list[index].txtStyle = txtStyle
      // 更新列表的狀態
      this.setData({
        curLists: list
      });
    }
  },
  touchE: function (e) {
    // console.log(`停止:` + JSON.stringify(e))
    var _this = this
    if(e.changedTouches.length === 1){
      // 手指移動結束後觸控點位置的X座標
      var endX = e.changedTouches[0].clientX
      // 觸控開始與結束,手指移動的距離
      var disX = _this.data.startX - endX
      var delBtnWidth = _this.data.delBtnWidth
      // 如果距離小於刪除按鈕的1/2,不顯示刪除按鈕
      var txtStyle = disX > delBtnWidth/2 ? `left:-` + delBtnWidth + `rpx` : `left:0`
      // 獲取手指觸控的是哪一項
      var index = e.currentTarget.dataset.index
      var list = _this.data.curLists
      list[index].txtStyle = txtStyle
      // 更新列表的狀態
      _this.setData({
        curLists: list
      });
    }
  }
  //...
})
複製程式碼

WXS實現時間格式

實現在效果如下:

小程式TodoList實踐

這裡我使用小程式的WXS

WXS(WeiXin Script)是小程式的一套指令碼語言,結合 WXML,可以構建出頁面的結構。瞭解詳細

新建一個api.wxs檔案,然後在index.wxml引用,定義模組名即可引用:

<!-- index.wxml -->
<wxs src="./api.wxs" module="api" />
...
<text class="time">{{api.formatTime(item.id)}}</text>
複製程式碼

每個 wxs 模組均有一個內建的 module 物件

api.wxs檔案和時間格式實現方法:

var formatTime = function(time){
  // 獲取當前時間
  var getUnix = function () {
    var date = getDate()
    return date.getTime()
  }
  // 獲取今天零點時間
  var getTodayUnix = function () {
    var date = getDate()
    date.setHours(0)
    date.setMinutes(0)
    date.setSeconds(0)
    date.setMilliseconds(0)
    return date.getTime()
  }
  // 獲取今年的1月1日零點時間
  var getYearUnix = function () {
    var date = getDate()
    date.setMonth(0)
    date.setDate(1)
    date.setHours(0)
    date.setMinutes(0)
    date.setSeconds(0)
    date.setMilliseconds(0)
    return date.getTime()
  }
  // 獲取標準時間
  var getLastDate = function (time) {
    var date = getDate(time)
    var month = date.getMonth() + 1 < 10 ? `0` + (date.getMonth() + 1) : date.getMonth() + 1
    var day = date.getDay() < 10 ? `0` + (date.getDay()) : date.getDay()
    return date.getFullYear() + `-` + month + `-` + day
  }
  // 轉換時間
  var getFormatTime = function (timestamp) {
    var now = getUnix()
    var today = getTodayUnix()
    var year = getYearUnix()
    var timer = (now - timestamp) / 1000
    var tip = ``
    if (timer <= 0) {
      tip = `剛剛`
    } else if (Math.floor(timer / 60) <= 0) {
      tip = `剛剛`
    } else if (timer < 3600) {
      tip = Math.floor(timer / 60) + `分鐘前`
    } else if (timer >= 3600 && (timestamp - today >= 0)) {
      tip = Math.floor(timer / 3600) + `小時前`
    } else if (timer / 86400 <= 31) {
      tip = Math.ceil(timer / 86400) + `天前`
    } else {
      tip = getLastDate(timestamp)
    }
    return tip
  }
  return getFormatTime(+time)
}

// es6方法一樣,匯出formatTime方法
module.exports.formatTime = formatTime;
複製程式碼

這裡需要注意的是獲取當前時間,WXS是不支援new Date(),它有自己本身的方法getDate()。使用方法跟new Date()一樣。

至此已完成,TodoList實踐例子,功能簡單,入門好例子。

希望對大家有幫助,不足的地方請大家指點,剛啃完官方文件擼的例子。

原碼下載:github.com/CH563/TodoL…

願大家都升職加薪!

相關文章