picker

picker ui 组件

首先我们需要思考规范组件的使用、提供那些可选参数、可响应哪些事件、以及提供哪些方法。

下面使用picker组件为例,来一步一步的完成自己的ui组件。

项目使用vue框架、按照同样的思路当然也可以切换到react、angular

首先我们来思考picker组件的使用场景,常见的有城市选择、多列联动选择等场景。常常是用户点击一个按钮弹出背景框和picker组。初始化相关样式、数据后,用户操作pickerItem,滑动选择数据,选定后确认或者取消事件。我们可以看到大致就是这样一个过程。

下面我们来一步一步的完成这个组件,最后并不断完善。

使用

1、每次picker被用户改变值的时候change事件
2、组件接受的参数数组 colcumns ,数组可以简单的一元数组/也可能成员为对象的数组
3、有哪些可自定义的参数? 如确定/取消/中间标题/是否显示toolbar等
哪些我们就先简单的列出组件的使用

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

组件html结构

先从最简单的开始我们需要一个picker展示的数据

先写组件的html结构,da-picker 是最外层,da-picker__toolbar是picker顶部的确定/取消按钮,da-column是picker外层里面包裹了pickerItem的数据项。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>

样式

在写一个ui组件的时候,样式库同样重要。这里先不对样式库进行处理,只是先将和picker组件相关的样式放在同一组件之内。

<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;
    }
  }
  // 公共样式需要抽离出来
  [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>

写完了页面结构和css后我们着重来思考整个组件的需要处理哪些事情。

数据初始化

<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 () {
      // 数据类型判断初始化结构数据
      const column = this.isSimple ? [{'values': this.columns}] : columns
    }
  }
}
</script>

样式初始化

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

初始化样式后将高度添加到da_columns 上。

下面我们将pickerItem渲染的内容抽象出来为单独的组件。这时我们修改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添加上数据、以及需要的touch相关事件.

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

状态改变

其中最核心的就是每次pickerItem的改变。我们给每一个Item一个index,setIndex(index, userAction) 为核心状态改变函数,userAction 为用户手动点击触摸选择改变,为用户触发一个change事件。

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

这样我们得到了picker最核心的一个方法。下面我们来分析每一次index改变时候需要进行的样式位置计算。

  • 初始化时候ul元素会有一个初始化baseOffset。随后每次index的改变ui的offset都为 offset + baseOffset。
  • 那么重点就是如何计算offset。
    • 我们每次操作的时候touchStart 需要记录当前的 firstOffset
    • 然后是touchMove移动的deltaY 此时offset = firstOffset + deltaY
    • 最后touchEnd结束的时候 通过offset的值计算出index,最后调用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 ,由于在前面已经对位置进行了初始化,ul上下可偏移的位置为自身位置加上一个itemHeight
},
onTouchEnd (event) {
  this.duration = 200
  const index = rang(Math.round(this.offset / this.itemHeight), [0, this.count - 1])
  this.setIndex(index, true)
}

通过touchStart获取初始化的位置,以及当前firstOffset,touchMove的时候计算this.offset的位置,并且限制最大滑动范围。最后通过touchEnd 计算出滑动后的Index,调用setIndex以及duration时间完成切换

未完

操作事件

修改完善

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351