微信小程序自定义picker

前言

之前UI丢了一张类似这样的效果图(当然这个是本文的效果图,不是原图,不过差不多)给我:
在这里插入图片描述
当时看到图就想这个还不简单,直接使用picker就行了。然后就是一顿操作,选择mode为date,最终出现了效果图:
在这里插入图片描述
正当我沉浸在完成功能的喜悦中的时候,心里有个声音一直在提醒我:你没有达到UI需求效果,你没有达到UI需求效果,你没有达到UI需求效果…

定睛一看果然和UI效果图有些许区别,UI左上角显示的是【全部】,而我们现在是【取消】,顶部是圆角,而我们现在是直角。第二个问题影响显然比第一个问题影响小,我们直接通过微信提供的picker相关API把左上角的字修改成【全部】勉强也能用,那我们就去查看下相关API。
在这里插入图片描述
查遍picker这货的所有API,竟然不支持修改左上角的文字。然后我们会妥协,让UI把这个全部功能去掉或者移动到其他地方么?不,这辈子都不可能妥协,程序猿永不为奴!!!既然这样,那我们就自己来实现个picker把,开干~~
在这里插入图片描述

实现

通过分析UI图可以发现,整个是由四部分组成:

  • 蒙层
  • 底部内容
  • 内容顶部按钮
  • picker滚动区域

蒙层

.mask {
    
    
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  background-color: rgba(0,0,0,0.7);
  z-index: 9999;
  flex-direction: column;
  justify-content: flex-end;
 }

z-index是设置显示层级,为了让内容显示在底部,使用column布局方式,并且justify-content为flex-end。这些知识在我之前系列的文章微信小程序布局技巧里面有提到,不太清楚的可以看之前的文章。

底部内容

 .content {
    
    
  display: flex;
  flex-direction: column;
  width: 100%;
  background: white;
  border-top-right-radius: 20rpx;
  border-top-left-radius: 20rpx;
}

布局是上下排列,所以使用column布局方式,然后通过border-top-right-radius和border-top-left-radius来实现左上角和右上角的圆角。

内容顶部

.top {
    
    
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 100rpx;
  border-bottom: 1rpx solid #d3cfcf;
}

.top-text {
    
    
  font-size: 30rpx;
  width: 150rpx;
  height: 100rpx;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

横向排列按钮,所以选用row布局,然后设置字体样式。

picker

这里主要是使用的系统提供的picekr-view实现,后面会给出完整代码。

布局实现

<view class='mask' wx:if="{
     
     {isShow}}" catchtap="cancel">
	<view class="content" style="height:500rpx" animation="{
     
     {animation}}">
		<view class="top">
			<view class="top-text top-left-color" hover-class="top-left-color-hover" catchtap="lefttap">全部</view>
			<view class="top-text top-right-color" hover-class="top-right-color-hover" catchtap="righttap">确定</view>
		</view>
		<picker-view style="width: 100%; height: 400rpx;" value="{
     
     {value}}" bindchange="bindChange" catchtap="no">
			<picker-view-column>
				<view wx:for="{
     
     {years}}" style="line-height: 50px" class="item">{
   
   {item}}年</view>
			</picker-view-column>
		</picker-view>
	</view>
</view>

js实现

我们这里为了更好的封装采用component形式:

var that
Component({
    
    
  /**
   * 组件的属性列表
   */
  properties: {
    
    
  	//开始年份
    start: {
    
    
      type: Number,
      value: 1900
    },
    //结束年份
    end: {
    
    
      type: Number,
      value: 9999
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    
    
    isShow: false,
    value: [0],//设置picker-view默认哪项选中
    years: [],
    year: 1900,
    current: 1900,
    dialogh: 0
  },
  attached: function () {
    
    
    console.log('attached')
    that = this
    //动画
    that.animation = wx.createAnimation({
    
    
      duration: 300
    })
    //500rpx转成px
    var dialoghpx = 500 / 750 * wx.getSystemInfoSync().windowWidth
    that.setData({
    
    
      dialogh: dialoghpx
    })
  },
  detached: function () {
    
    
    console.log('detached')
  },
  pageLifetimes: {
    
    
    //组件所在的页面被展示时执行 最低版本2.2.3
    show: function () {
    
    
      console.log('页面show')
      var yearstemp = []
      for(var i = that.properties.start; i <= that.properties.end; i ++){
    
    
        yearstemp.push(i)
      }
      //通过配置属性获取years
      that.setData({
    
    
        years: yearstemp
      })
    },
    //组件所在的页面被隐藏时执行 最低版本2.2.3
    hide: function () {
    
    
      console.log('页面被隐藏')
    },
    //这个函数一般使用场景较少,了解就可以了  最低版本2.4.0
    resize: function (size) {
    
    
      console.log('页面尺寸变化')
    }
  },
  /**
   * 组件的方法列表
   */
  methods: {
    
    
    bindChange: function (e) {
    
    
      const val = e.detail.value
      that.setData({
    
    
        year: this.data.years[val[0]]
      })
    },
    //这个current不放properties中是因为properties中的属性只会初始化一次,而这里需要动态改变
    showDialog(current){
    
    
      that.setData({
    
    
        isShow: true
      })
      var currentindex = 0
      //筛选出默认选择项目
      that.data.years.forEach(function(v,i,s){
    
    
        if(current == v){
    
    
          currentindex = i
        }
      })
      that.setData({
    
    
        [`value[0]`] : currentindex,
        year: that.data.years[currentindex],
        current: current
      })
	  //先向下移动dialog高度,然后恢复原位从而形成从下向上弹出效果
      that.animation.translateY(that.data.dialogh).translateY(0).step()
      that.setData({
    
    animation: that.animation.export()})
    },
    cancel(){
    
    
      //绑定cancel事件
      this.triggerEvent("cancel")
      that.dimsss()
    },
    dimsss(){
    
    
      //从原位向下移动dailog高度,形成从上向下的收起效果
      that.animation.translateY(that.data.dialogh).step()
      that.setData({
    
    animation: that.animation.export()})
      //动画结束后蒙层消失
      setTimeout(() => {
    
    
        that.setData({
    
    
          isShow: false
        })
     }, 300)
    },
    lefttap(){
    
    
      //绑定lefttap事件
      this.triggerEvent("lefttap")
      that.dimsss()
    },
    righttap(){
    
    
      //绑定righttap事件
      this.triggerEvent("righttap",{
    
    
        year: that.data.year
      })
      that.dimsss()
    }
  }
})

样式

.mask {
    
    
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  background-color: rgba(0,0,0,0.7);
  z-index: 9999;
  flex-direction: column;
  justify-content: flex-end;
 }

 .content {
    
    
  display: flex;
  flex-direction: column;
  width: 100%;
  background: white;
  border-top-right-radius: 20rpx;
  border-top-left-radius: 20rpx;
}

.top {
    
    
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 100rpx;
  border-bottom: 1rpx solid #d3cfcf;
}

.top-text {
    
    
  font-size: 30rpx;
  width: 150rpx;
  height: 100rpx;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.top-left-color {
    
    
  color: #878787;
}

.top-left-color-hover {
    
    
  color: #f1eaea;
}

.top-right-color {
    
    
  color: #1296DB;
}

.top-right-color-hover {
    
    
  color: #82ccf3;
}

.item {
    
    
  width: 100%;
  align-items: center;
  justify-content: center;
  display: flex;
  flex-direction: row;
  font-size: 35rpx;
}

使用姿势

json中引用组件:

"usingComponents": {
    
    
    "custom-picker":"../component/datepickerview/datepickerview"
  }

布局文件:

<button bindtap="showpicker">自定义picker</button>
<custom-picker id="custom-picker" start="2018" end="2020" bind:lefttap='_lefttap' bind:righttap='_righttap' bindcancel="_cancel"/>

js文件:

const app = getApp()
var that
Page({
    
    
  data: {
    
    
    current: 2019
  },
  onLoad: function () {
    
    
    that = this
  },
  onReady: function () {
    
    
    that.picker = that.selectComponent("#custom-picker")
  },
  showpicker(){
    
    
    that.picker.showDialog(that.data.current)
  },
  _lefttap(){
    
    
    wx.showToast({
    
    
      title: '左边按钮被点击',
      icon: 'none'
    })
  },
  _righttap(e){
    
    
    var year = e.detail.year
    wx.showToast({
    
    
      title: '右边按钮被点击,选择的值为:'+year,
      icon: 'none'
    })
    //保存选择的年份
    that.setData({
    
    
      current:year
    })
  },
  _cancel(){
    
    
    wx.showToast({
    
    
      title: 'picker被取消',
      icon: 'none'
    })
  }
})

最终效果图:
在这里插入图片描述

代码获取

如果你想要获取完整代码
请戳↓↓↓↓
点击用微信开发者工具打开

尾巴

这里还是要吐槽下微信小程序的API设计,这么一个简单的API都没有提供给开发者,为了这么一个小功能还得自己整一个picker。不过虽然折腾了下,我怎么觉得自定义的这个picker比官方的更好看,哈哈。
在这里插入图片描述

文章中关键位置已经做好注释,如果有问题欢迎留言。
老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!

猜你喜欢

转载自blog.csdn.net/abs625/article/details/107786845