vue 实现 ios 原生picker 效果及实现思路解析


Posted in Javascript onDecember 06, 2017

以前最早实现了一个类似的时间选择插件,但是适用范围太窄,索性最近要把这个实现方式发布出来,就重写了一个高复用的vue组件。

支持安卓4.0以上,safari 7以上

vue 实现 ios 原生picker 效果及实现思路解析

效果预览

gitHub

滚轮部分主要dom结构

<template data-filtered="filtered">
 <div class="pd-select-item">
  <div class="pd-select-line"></div>
  <ul class="pd-select-list">
   <li class="pd-select-list-item">1</li>
  </ul>
  <ul class="pd-select-wheel">
   <li class="pd-select-wheel-item">1</li>
  </ul>
 </div>
</template>
props
props: {
   data: {
    type: Array,
    required: true
   },
   type: {
    type: String,
    default: 'cycle'
   },
   value: {}
  }

设置css样式 使其垂直居中

.pd-select-line, .pd-select-list, .pd-select-wheel {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}
.pd-select-list {
  overflow: hidden;
}

滚轮3d样式设置

/* 滚轮盒子 */
.pd-select-wheel {
  transform-style: preserve-3d;
  height: 30px;
}
/* 滚轮单项 */
.pd-select-wheel-item {
  white-space: nowrap;
  text-overflow: ellipsis;
  backface-visibility: hidden;
  position: absolute;
  top: 0px;
  width: 100%;
  overflow: hidden;
}

vue 实现 ios 原生picker 效果及实现思路解析

主要注意2个属性 transform-style: preserve-3d; backface-visibility: hidden;

第一个是3d布局,让界面3D化,第二个是让滚轮背后自动隐藏(上图红色部分,背面的dom节点 会自动隐藏)

如何实现3D 滚轮

盒子主要这句css transform: rotate3d(1, 0, 0, x deg);

item主要运用这句css transform: rotate3d(1, 0, 0, xdeg) translate3d(0px, 0px, [x]px);

vue 实现 ios 原生picker 效果及实现思路解析

vue 实现 ios 原生picker 效果及实现思路解析
vue 实现 ios 原生picker 效果及实现思路解析

上面2张图展示了translate3d(0px, 0px, [x]px);这句话的效果 [x]就是圆的半径

vue 实现 ios 原生picker 效果及实现思路解析

从上面的图可以看见,我们只需旋转每个dom自身,然后利用translate3d(0px, 0px, [x]px);把每个dom扩展开

就形成了圆环.α就是每个dom自身旋转的角度,因为这里只用了0到180°,所以用了个盒子在装这些dom

行高 和角度计算

vue 实现 ios 原生picker 效果及实现思路解析

已知两边和夹角 算第三边长度 ~=34px

http://tool.520101.com/calculator/sanjiaoxingjiaodu/

无限滚轮实现

/* 滚轮展示大小限定 */
spin: {start: 0, end: 9, branch: 9}
 
/* 获取spin 数据 */
 getSpinData (index) {
  index = index % this.listData.length
  return this.listData[index >= 0 ? index : index + this.listData.length]
 }
 /* 模运算 获取数组有的索引 这样就构成 圆环了 */

touchend做特殊处理

在touchend 里设置setCSS类型 把滚动数据取整,这样停止的时候就是

一格一格的准确转动到位

// other code ....
/* 计算touchEnd移动的整数距离 */
    let endMove = margin
    let endDeg = Math.round(updateDeg / deg) * deg
    if (type === 'end') {
     this.setListTransform(endMove, margin)
     this.setWheelDeg(endDeg)
    } else {
     this.setListTransform(updateMove, margin)
     this.setWheelDeg(updateDeg)
    }
 // other code ....
惯性缓动
// other code ....
setWheelDeg (updateDeg, type, time = 1000) {
    if (type === 'end') {
     this.$refs.wheel.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`
     this.$refs.wheel.style.webkitTransform = `rotate3d(1, 0, 0, ${updateDeg}deg)`
    } else {
     this.$refs.wheel.style.webkitTransition = ''
     this.$refs.wheel.style.webkitTransform = `rotate3d(1, 0, 0, ${updateDeg}deg)`
    }
   }
setListTransform (translateY = 0, marginTop = 0, type, time = 1000) {
    if (type === 'end') {
     this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`
     this.$refs.list.style.webkitTransform = `translateY(${translateY - this.spin.branch * 34}px)`
     this.$refs.list.style.marginTop = `${-marginTop}px`
     this.$refs.list.setAttribute('scroll', translateY)
     console.log('end')
    } else {
     this.$refs.list.style.webkitTransition = ''
     this.$refs.list.style.webkitTransform = `translateY(${translateY - this.spin.branch * 34}px)`
     this.$refs.list.style.marginTop = `${-marginTop}px`
     this.$refs.list.setAttribute('scroll', translateY)
    }
}
// other code ....

获取当前选中值

/* 在设置完css后获取值 */
setStyle (move, type, time) {
  // ...other code
  /* 设置$emit 延迟 */
  setTimeout(() => this.getPickValue(endMove), 1000)
 // ...other code
}
/* 获取选中值 */
   getPickValue (move) {
    let index = Math.abs(move / 34)
    let pickValue = this.getSpinData(index)
    this.$emit('input', pickValue)
   }

初始化设置

mounted () {
   /* 事件绑定 */
   this.$el.addEventListener('touchstart', this.itemTouchStart)
   this.$el.addEventListener('touchmove', this.itemTouchMove)
   this.$el.addEventListener('touchend', this.itemTouchEnd)
   /* 初始化状态 */
   let index = this.listData.indexOf(this.value)
   if (index === -1) {
    console.warn('当前初始值不存在,请检查后listData范围!!')
    this.setListTransform()
    this.getPickValue(0)
   } else {
    let move = index * 34
    /* 因为往上滑动所以是负 */
    this.setStyle(-move)
    this.setListTransform(-move, -move)
   }

当展示为非无限滚轮的时

这里我们很好判断,就是滚动的距离不能超过原始数的数组长度*34,且不能小于0(实际代码中涉及方向)

/* 根据滚轮类型 line or cycle 判断 updateMove最大距离 */
    if (this.type === 'line') {
     if (updateMove > 0) {
      updateMove = 0
     }
     if (updateMove < -(this.listData.length - 1) * singleHeight) {
      updateMove = -(this.listData.length - 1) * singleHeight
     }
    }
 /* 根据type 控制滚轮显示效果 */
   setHidden (index) {
    if (this.type === 'line') {
     return index < 0 || index > this.listData.length - 1
    } else {
     return false
    }
   },

dom结构也增加了对应的响应

<div class="pd-select-item">
  <div class="pd-select-line"></div>
  <div class="pd-select-list">
   <ul class="pd-select-ul" ref="list">
    <li class="pd-select-list-item" v-for="el,index in renderData " :class="{'hidden':setHidden(el.index)}" :key="index">{{el.value}}</li>
   </ul>
  </div>
  <ul class="pd-select-wheel" ref="wheel">
   <li class="pd-select-wheel-item" :class="{'hidden':setHidden(el.index)}" :style="setWheelItemDeg(el.index)" :index="el.index" v-for="el,index in renderData " :key="index">{{el.value}}</li>
  </ul>
 </div>

总结

以上所述是小编给大家介绍的vue 实现 ios 原生picker 效果及思路解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery ui1.7 dialog只能弹出一次问题
Aug 27 Javascript
javascript权威指南 学习笔记之变量作用域分享
Sep 28 Javascript
Jquery实现仿新浪微博获取文本框能输入的字数代码
Feb 22 Javascript
javascript对下拉列表框(select)的操作实例讲解
Nov 29 Javascript
html的DOM中document对象anchors集合用法实例
Jan 21 Javascript
javascript常用函数(1)
Nov 04 Javascript
JavaScript中校验银行卡号的实现代码
Dec 19 Javascript
详解Vue-cli代理解决跨域问题
Sep 27 Javascript
详解webpack 入门与解析
Apr 09 Javascript
Vue中的字符串模板的使用
May 17 Javascript
layui问题之模拟select点击事件的实例讲解
Aug 15 Javascript
解决vue页面刷新,数据丢失的问题
Nov 24 Vue.js
微信小程序实现点击按钮修改文字大小功能【附demo源码下载】
Dec 06 #Javascript
基于Vue2的独立构建与运行时构建的差别(详解)
Dec 06 #Javascript
js 两数组去除重复数值的实例
Dec 06 #Javascript
js 提取某()特殊字符串长度的实例
Dec 06 #Javascript
React-Router如何进行页面权限管理的方法
Dec 06 #Javascript
移动前端图片压缩上传的实例
Dec 06 #Javascript
详细分析JS函数去抖和节流
Dec 05 #Javascript
You might like
德生PL450的电路分析和低放电路的改进办法
2021/03/02 无线电
最简单的PHP程序--记数器
2006/10/09 PHP
PHP 获取MSN好友列表的代码(2009-05-14测试通过)
2009/09/09 PHP
jquery实现居中弹出层代码
2010/08/25 Javascript
window.onload追加函数使用示例
2014/03/03 Javascript
js中的onchange和onpropertychange (onchange无效的解决方法)
2014/03/08 Javascript
jQuery 浮动导航菜单适合购物商品类型的网站
2014/09/09 Javascript
jQuery中outerWidth()方法用法实例
2015/01/19 Javascript
jquery.map()方法的使用详解
2015/07/09 Javascript
Jquery效果大全之制作电脑健康体检得分特效附源码下载
2015/11/02 Javascript
快速掌握Node.js模块封装及使用
2016/03/21 Javascript
脚本div实现拖放功能(两种)
2017/02/13 Javascript
js中获取键盘按下键值event.keyCode、event.charCode和event.which的兼容性详解
2017/03/15 Javascript
jquery 禁止鼠标右键并监听右键事件
2017/04/27 jQuery
如何解决js函数防抖、节流出现的问题
2019/06/17 Javascript
JSONP解决JS跨域问题的实现
2020/05/25 Javascript
js在HTML的三种引用方式详解
2020/08/29 Javascript
微信小程序实现modal弹出框遮罩层组件(可带文本框)
2020/12/20 Javascript
Python中第三方库Requests库的高级用法详解
2017/03/12 Python
Django验证码的生成与使用示例
2017/05/20 Python
解决PyCharm的Python.exe已经停止工作的问题
2018/11/29 Python
解决python打不开文件(文件不存在)的问题
2019/02/18 Python
Python使用修饰器进行异常日志记录操作示例
2019/03/19 Python
Python转换itertools.chain对象为数组的方法
2020/02/07 Python
python GUI库图形界面开发之PyQt5简单绘图板实例与代码分析
2020/03/08 Python
关于Python3的import问题(pycharm可以运行命令行import错误)
2020/11/18 Python
python中Pexpect的工作流程实例讲解
2021/03/02 Python
canvas拼图功能实现代码示例
2018/11/21 HTML / CSS
您的网上新华书店:文轩网
2016/08/24 全球购物
小学生母亲节演讲稿
2014/05/07 职场文书
卫生保健工作总结2015
2015/05/18 职场文书
2015年网络管理员工作总结
2015/05/21 职场文书
html+css实现分层金字塔的实例
2021/06/02 HTML / CSS
《杜鹃的婚约》OP主题曲「凸凹」无字幕影像公开
2022/04/08 日漫
原生JS实现分页
2022/04/19 Javascript
python解析照片拍摄时间进行图片整理
2022/07/23 Python