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 相关文章推荐
javascript parseInt 函数分析(转)
Mar 21 Javascript
js focus不起作用的解决方法(主要是因为dom元素是否加载完成)
Nov 05 Javascript
基于jquery自定义的漂亮单选按钮RadioButton
Nov 19 Javascript
js 异步操作回调函数如何控制执行顺序
Dec 24 Javascript
js调试工具console.log()方法查看js代码的执行情况
Aug 08 Javascript
常用的JavaScript WEB操作方法分享
Feb 28 Javascript
使用iojs的jsdom库实现同步系统时间
Apr 20 Javascript
JS生成一维码(条形码)功能示例
Jan 19 Javascript
JavaScript实现获取远程的html到当前页面中
Mar 26 Javascript
js HTML5 canvas绘制图片的方法
Sep 08 Javascript
Vue2(三)实现子菜单展开收缩,带动画效果实现方法
Apr 28 Javascript
浅入深出Vue之组件使用
Jul 11 Javascript
微信小程序实现点击按钮修改文字大小功能【附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
PHP 文件上传功能实现代码
2009/06/24 PHP
浅析PHP7新功能及语法变化总结
2016/06/17 PHP
PHP7 其他语言层面的修改
2021/03/09 PHP
用js怎么把&amp;字符换成&quot;&amp;amp:&quot;
2006/10/19 Javascript
jQuery ui插件的使用方法代码实例
2013/05/08 Javascript
用html+css+js实现的一个简单的图片切换特效
2014/05/28 Javascript
以JavaScript来实现WordPress中的二级导航菜单的方法
2015/12/14 Javascript
Google Maps基础及实例解析
2016/08/06 Javascript
jquery自动补齐功能插件flexselect用法示例
2016/08/06 Javascript
详解angular中通过$location获取路径(参数)的写法
2017/03/21 Javascript
详解用node编写自己的cli工具
2017/05/23 Javascript
深入理解Angular4订阅(Subscribe)与取消
2017/11/22 Javascript
jQuery实现获取动态添加的标签对象示例
2018/06/28 jQuery
详解在React.js中使用PureComponent的重要性和使用方式
2018/07/10 Javascript
改进 JavaScript 和 Rust 的互操作性并深入认识 wasm-bindgen 组件
2019/07/13 Javascript
微信小程序反编译的实现
2020/12/10 Javascript
wxpython中利用线程防止假死的实现方法
2014/08/11 Python
在主机商的共享服务器上部署Django站点的方法
2015/07/22 Python
Python基于hashlib模块的文件MD5一致性加密验证示例
2018/02/10 Python
python 获取list特定元素下标的实例讲解
2018/04/09 Python
Python 最强编辑器详细使用指南(PyCharm )
2019/09/16 Python
python 视频逐帧保存为图片的完整实例
2019/12/10 Python
如何基于Python实现电子邮件的发送
2019/12/16 Python
Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中
2020/01/13 Python
Jupyter Notebook 实现正常显示中文和负号
2020/04/24 Python
python Xpath语法的使用
2020/11/26 Python
HTML5 canvas基本绘图之绘制线段
2016/06/27 HTML / CSS
带你认识HTML5中的WebSocket
2015/05/22 HTML / CSS
俄罗斯运动、健康和美容产品在线商店:Lactomin.ru
2020/07/23 全球购物
日期和时间问题
2015/01/04 面试题
一套Delphi的笔试题一
2016/02/14 面试题
道德之星事迹材料
2014/05/03 职场文书
水利水电专业自荐信
2014/07/08 职场文书
市场营销计划书范文
2015/01/16 职场文书
MySQL分区表管理命令汇总
2022/03/21 MySQL
Dashboard管理Kubernetes集群与API访问配置
2022/04/01 Servers