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 按钮状态效果 正常、移上、按下
Aug 12 Javascript
jQuery CSS()方法改变现有的CSS样式表
Sep 09 Javascript
jQuery实现简单的间隔向上滚动效果
Mar 09 Javascript
jQuery实现MSN中文网滑动Tab菜单效果代码
Sep 09 Javascript
通过js修改input、select默认字体颜色
Apr 19 Javascript
微信小程序在地图选择地址并返回经纬度简单示例
Dec 03 Javascript
inquirer.js一个用户与命令行交互的工具详解
May 18 Javascript
Vue中img的src是动态渲染时不显示的解决
Nov 14 Javascript
javascript设计模式 ? 解释器模式原理与用法实例分析
Apr 17 Javascript
js最全的数组的降维5种办法(小结)
Apr 28 Javascript
jQuery 选择方法及$(this)用法实例分析
May 19 jQuery
three.js欧拉角和四元数的使用方法
Jul 26 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 $_SERVER[&quot;REQUEST_URI&quot;]获取值的通用解决方法
2010/06/21 PHP
php自定义函数call_user_func和call_user_func_array详解
2011/07/14 PHP
帝国cms目录结构分享
2015/07/06 PHP
Thinkphp3.2.3分页使用实例解析
2016/07/28 PHP
php支付宝APP支付功能
2020/07/29 PHP
JS特殊函数(Function()构造函数、函数直接量)区别介绍
2013/05/19 Javascript
一个简单的弹性返回顶部JS代码实现介绍
2013/06/09 Javascript
导入extjs、jquery 文件时$使用冲突问题解决方法
2014/01/14 Javascript
jQuery实现不断闪烁文字的方法
2015/05/15 Javascript
详解javascript中原始数据类型Null和Undefined
2015/12/17 Javascript
详解maxlength属性在textarea里奇怪的表现
2015/12/27 Javascript
vue中倒计时组件的实例代码
2018/07/06 Javascript
LayUi使用switch开关,动态的去控制它是否被启用的方法
2019/09/21 Javascript
详解微信小程序中var、let、const用法与区别
2020/01/11 Javascript
ES6学习笔记之字符串、数组、对象、函数新增知识点实例分析
2020/01/22 Javascript
React+EggJs实现断点续传的示例代码
2020/07/07 Javascript
详谈vue中router-link和传统a链接的区别
2020/07/22 Javascript
[05:03]2018DOTA2亚洲邀请赛主赛事首日回顾
2018/04/04 DOTA
python 获取本机ip地址的两个方法
2013/02/25 Python
使用Python的Django框架结合jQuery实现AJAX购物车页面
2016/04/11 Python
放弃 Python 转向 Go语言有人给出了 9 大理由
2017/10/20 Python
python 3.0 模拟用户登录功能并实现三次错误锁定
2017/11/01 Python
PyQt5打开文件对话框QFileDialog实例代码
2018/02/07 Python
全球知名鞋履品牌授权零售商:Journeys
2016/09/17 全球购物
印度网上药店:1mg
2017/10/13 全球购物
通信工程专业个人找工作求职信范文
2013/09/21 职场文书
教师个人的自我评价分享
2014/01/02 职场文书
机械制造毕业生求职信
2014/03/03 职场文书
小学英语课后反思
2014/04/26 职场文书
爱心捐书活动总结
2014/07/05 职场文书
爱牙日宣传活动总结
2015/02/05 职场文书
教师个人师德总结
2015/02/06 职场文书
力克胡哲观后感
2015/06/10 职场文书
国家助学金受助感言
2015/08/01 职场文书
一篇文章带你掌握SQLite3基本用法
2022/06/14 数据库
CSS中calc(100%-100px)不加空格不生效
2023/05/07 HTML / CSS