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 相关文章推荐
删除重复数据的算法
Nov 23 Javascript
juqery 学习之五 文档处理 包裹、替换、删除、复制
Feb 11 Javascript
js实现温度计时间样式代码分享
Aug 21 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
Mar 02 Javascript
JavaScript无缝滚动效果的实例代码
Mar 27 Javascript
jquery DataTable实现前后台动态分页
Jun 17 jQuery
JS加密插件CryptoJS实现AES加密操作示例
Aug 16 Javascript
原生JavaScript之es6中Class的用法分析
Feb 23 Javascript
Vue vm.$attrs使用场景详解
Mar 08 Javascript
React中Ref 的使用方法详解
Apr 28 Javascript
解决vue的router组件component在import时不能使用变量问题
Jul 26 Javascript
微信小程序实现分页加载效果
Nov 19 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
星际中的相关伤害
2020/03/04 星际争霸
php+dbfile开发小型留言本
2006/10/09 PHP
PHP+redis实现的悲观锁机制示例
2018/06/12 PHP
一些主流JS框架中DOMReady事件的实现小结
2011/02/12 Javascript
javascript实现俄罗斯方块游戏的思路和方法
2015/04/27 Javascript
javascript中call apply 与 bind方法详解
2016/03/10 Javascript
JS上传图片预览插件制作(兼容到IE6)
2016/08/07 Javascript
js实现无缝循环滚动
2020/06/23 Javascript
vue实现简单表格组件实例详解
2017/04/16 Javascript
详解Vue2.X的路由管理记录之 钩子函数(切割流水线)
2017/05/02 Javascript
详解nuxt sass全局变量(公共scss解决方案)
2018/06/27 Javascript
微信小程序url传参写变量的方法
2018/08/09 Javascript
jQuery实现简单的Ajax调用功能示例
2019/02/15 jQuery
详解如何理解vue的key属性
2019/04/14 Javascript
ng-alain的sf如何自定义部件的流程
2020/06/12 Javascript
js实现扫雷源代码
2020/11/27 Javascript
[09:23]国际邀请赛采访专栏:iG战队VK,Tongfu战队Cu
2013/08/05 DOTA
[01:02:46]VGJ.S vs NB 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
详解在Python的Django框架中创建模板库的方法
2015/07/20 Python
详解python3百度指数抓取实例
2016/12/12 Python
解决Python requests 报错方法集锦
2017/03/19 Python
Python3爬虫爬取百姓网列表并保存为json功能示例【基于request、lxml和json模块】
2018/12/05 Python
Python 3.8中实现functools.cached_property功能
2019/05/29 Python
Keras 使用 Lambda层详解
2020/06/10 Python
Python 字典一个键对应多个值的方法
2020/09/29 Python
香港彩色隐形眼镜在线商店:Stunninglens(全球免费送货)
2019/05/10 全球购物
日本最大的彩色隐形眼镜销售网站:CharmColor
2020/09/09 全球购物
介绍一下木马病毒的种类
2015/07/26 面试题
ShellScript面试题一则-ShellScript编程
2014/03/05 面试题
计算机大学生的自我评价
2013/10/15 职场文书
授权委托书格式模板
2014/04/03 职场文书
工程承诺书怎么写
2014/05/24 职场文书
声乐专业大学生职业生涯规划书:理想的未来需要自己去打造
2014/09/20 职场文书
大学生入党自荐书
2015/03/05 职场文书
Python pandas读取CSV文件的注意事项(适合新手)
2021/06/20 Python
SpringBoot中使用Redis作为全局锁示例过程
2022/03/24 Java/Android