picker

picker ui 組件

首先我們需要思考規(guī)范組件的使用、提供那些可選參數(shù)授嘀、可響應(yīng)哪些事件演侯、以及提供哪些方法第租。

下面使用picker組件為例膀息,來(lái)一步一步的完成自己的ui組件般眉。

項(xiàng)目使用vue框架、按照同樣的思路當(dāng)然也可以切換到react潜支、angular

首先我們來(lái)思考picker組件的使用場(chǎng)景甸赃,常見(jiàn)的有城市選擇、多列聯(lián)動(dòng)選擇等場(chǎng)景冗酿。常常是用戶點(diǎn)擊一個(gè)按鈕彈出背景框和picker組埠对。初始化相關(guān)樣式、數(shù)據(jù)后裁替,用戶操作pickerItem项玛,滑動(dòng)選擇數(shù)據(jù),選定后確認(rèn)或者取消事件弱判。我們可以看到大致就是這樣一個(gè)過(guò)程襟沮。

下面我們來(lái)一步一步的完成這個(gè)組件,最后并不斷完善昌腰。

使用

1开伏、每次picker被用戶改變值的時(shí)候change事件
2、組件接受的參數(shù)數(shù)組 colcumns 遭商,數(shù)組可以簡(jiǎn)單的一元數(shù)組/也可能成員為對(duì)象的數(shù)組
3固灵、有哪些可自定義的參數(shù)? 如確定/取消/中間標(biāo)題/是否顯示toolbar等
哪些我們就先簡(jiǎn)單的列出組件的使用

  <picker 
    @change="onChange()" 
    :columns="columns" 
    :showToolbar="true" 
    :title="title">
  </picker>

組件html結(jié)構(gòu)

先從最簡(jiǎn)單的開(kāi)始我們需要一個(gè)picker展示的數(shù)據(jù)

先寫組件的html結(jié)構(gòu),da-picker 是最外層劫流,da-picker__toolbar是picker頂部的確定/取消按鈕巫玻,da-column是picker外層里面包裹了pickerItem的數(shù)據(jù)項(xiàng)。da-iframe是picker選中的橫條祠汇,固定位置的上下1px的橫條大审。

<template>
  <div class="da-picker">
    <div class="da-picker__toolbar">
    </div>
    <div class="da-picker__column" :style="calcHeight">
      <div>
        <ul>
          <li>item1</li>
          <li>item2</li>
          <li>item3</li>
        </ul>
      </div>
      <div class="da-iframe da-1pxborder--top-bottom"></div>
    </div>
  </div>
</template>

樣式

在寫一個(gè)ui組件的時(shí)候,樣式庫(kù)同樣重要座哩。這里先不對(duì)樣式庫(kù)進(jìn)行處理徒扶,只是先將和picker組件相關(guān)的樣式放在同一組件之內(nèi)。

<style lang="scss">
  .da-picker{
    overflow: hidden;
    position: relative;
    .da-picker__toolbar{
      display: flex;
      justify-content: space-between;
    }
    .da-column{
      position: relative;
      overflow: hidden;
    }
    .da-iframe{
      left: 0;
      top: 0;
      width: 100%;
      position: absolute;
      transform: translateY(-50%);
      border: 1px 0;
    }
  }
  // 公共樣式需要抽離出來(lái)
  [class*='da-1pxborder']::after{
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 200%;
    height: 200%;
    transform: scale(.5);
    pointer-event: none;
    border: 0px solid #e5e5e5;
  }
<style>

寫完了頁(yè)面結(jié)構(gòu)和css后我們著重來(lái)思考整個(gè)組件的需要處理哪些事情根穷。

數(shù)據(jù)初始化

<script>
export default {
  name: 'picker',
  props: {
    columns: {
      type: Array,
      default: () => []
    },
    count: {
      type: Number,
      default: 5
    }
  },
  data () {
    return {
      isSimple: false
    }
  },
  computed: {
    isSimple () {
      return this.columns.length && !this.columns[0].values
    }
  }
  created () {
    this.initColumn()
  },
  methods: {
    initColumn () {
      // 數(shù)據(jù)類型判斷初始化結(jié)構(gòu)數(shù)據(jù)
      const column = this.isSimple ? [{'values': this.columns}] : columns
    }
  }
}
</script>

樣式初始化

computed: {
  calcHeight () {
    return {
      height: (this.count - 1) * this.itemHeight // props itemHeight
    }
  }
}

初始化樣式后將高度添加到da_columns 上姜骡。

下面我們將pickerItem渲染的內(nèi)容抽象出來(lái)為單獨(dú)的組件。這時(shí)我們修改picker的組件為

<template>
  <div class="da-picker">
    <div class="da-picker__toolbar">
    </div>
    <div class="da-picker__column" :style="calcHeight">
      <pickItem :column="column"></pickItem>
      <div class="da-iframe da-1pxborder--top-bottom"></div>
    </div>
  </div>
</template>
<template>
  <div>
    <ul>
      <li>item1</li>
      <li>item2</li>
      <li>item3</li>
    </ul>
  </div>
</template>

為上面的html添加上數(shù)據(jù)屿良、以及需要的touch相關(guān)事件.

<template>
  <div>
    <ul 
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd"
      @touchcancel="onTouchEnd"
    >
      <li v-for="item in options" :key="item">{{item}}</li>
    </ul>
  </div>
</template>

狀態(tài)改變

其中最核心的就是每次pickerItem的改變圈澈。我們給每一個(gè)Item一個(gè)index,setIndex(index, userAction) 為核心狀態(tài)改變函數(shù)尘惧,userAction 為用戶手動(dòng)點(diǎn)擊觸摸選擇改變,為用戶觸發(fā)一個(gè)change事件康栈。

setIndex (index, userAction) {
  this.offset = - index * this.itemHeight
  if(index !== this.currentIndex) {
    userAction && this.$emit('change', index)
  }
}

這樣我們得到了picker最核心的一個(gè)方法。下面我們來(lái)分析每一次index改變時(shí)候需要進(jìn)行的樣式位置計(jì)算。

  • 初始化時(shí)候ul元素會(huì)有一個(gè)初始化baseOffset啥么。隨后每次index的改變ui的offset都為 offset + baseOffset登舞。
  • 那么重點(diǎn)就是如何計(jì)算offset。
    • 我們每次操作的時(shí)候touchStart 需要記錄當(dāng)前的 firstOffset
    • 然后是touchMove移動(dòng)的deltaY 此時(shí)offset = firstOffset + deltaY
    • 最后touchEnd結(jié)束的時(shí)候 通過(guò)offset的值計(jì)算出index悬荣,最后調(diào)用setIndex(index, userAction)

用戶操作

onTouchStart (event) {
  this.startY = event.touches[0].clientY
  this.firstOffset = this.offset
  this.duration = 0
},
onTouchMove (event) {
  const deltaY = event.touches[0].clientY - this.startY
  this.offset = range(this.firstOffset + deltaY, [- this.itemHeight * this.count, this.itemHeight ]) // 最大offset 范圍需要注意菠秒,ul 高度為 (this.count - 1) * this.itemHeight ,由于在前面已經(jīng)對(duì)位置進(jìn)行了初始化,ul上下可偏移的位置為自身位置加上一個(gè)itemHeight
},
onTouchEnd (event) {
  this.duration = 200
  const index = rang(Math.round(this.offset / this.itemHeight), [0, this.count - 1])
  this.setIndex(index, true)
}

通過(guò)touchStart獲取初始化的位置氯迂,以及當(dāng)前firstOffset践叠,touchMove的時(shí)候計(jì)算this.offset的位置,并且限制最大滑動(dòng)范圍嚼蚀。最后通過(guò)touchEnd 計(jì)算出滑動(dòng)后的Index禁灼,調(diào)用setIndex以及duration時(shí)間完成切換

未完

操作事件

修改完善

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市轿曙,隨后出現(xiàn)的幾起案子弄捕,更是在濱河造成了極大的恐慌,老刑警劉巖拳芙,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察藐,死亡現(xiàn)場(chǎng)離奇詭異皮璧,居然都是意外死亡舟扎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門悴务,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)睹限,“玉大人,你說(shuō)我怎么就攤上這事讯檐∠哿疲” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵别洪,是天一觀的道長(zhǎng)叨恨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)挖垛,這世上最難降的妖魔是什么痒钝? 我笑而不...
    開(kāi)封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮痢毒,結(jié)果婚禮上送矩,老公的妹妹穿的比我還像新娘。我一直安慰自己哪替,他們只是感情好栋荸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般晌块。 火紅的嫁衣襯著肌膚如雪爱沟。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天摸袁,我揣著相機(jī)與錄音钥顽,去河邊找鬼。 笑死靠汁,一個(gè)胖子當(dāng)著我的面吹牛蜂大,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝶怔,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼奶浦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了踢星?” 一聲冷哼從身側(cè)響起澳叉,我...
    開(kāi)封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沐悦,沒(méi)想到半個(gè)月后成洗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藏否,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年瓶殃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片副签。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遥椿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淆储,到底是詐尸還是另有隱情冠场,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布本砰,位于F島的核電站碴裙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏点额。R本人自食惡果不足惜舔株,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咖楣。 院中可真熱鬧督笆,春花似錦、人聲如沸诱贿。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至料扰,卻和暖如春凭豪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晒杈。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工嫂伞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拯钻。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓帖努,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親粪般。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拼余,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351