先放个效果图,不是你要的效果,你也就别继续往下看了。
贴个代码:
.mf-picker {
position: fixed;
left: 0;
top: 0;
z-index: 100;
width: 100%;
height: 100%;
overflow: hidden;
text-align: center;
font-size: 14px;
background-color: rgba(37, 38, 45, .4);
}
.mf-picker .mf-picker-panel {
position: absolute;
z-index: 600;
bottom: 0;
width: 100%;
height: 273px;
background: #fff;
}
.mf-picker .mf-picker-panel .mf-picker-choose {
position: relative;
height: 60px;
color: #999;
}
.mf-picker .mf-picker-panel .mf-picker-content {
position: relative;
top: 20px;
height: 173px;
}
.border-bottom-1px,
.border-top-1px {
position: relative;
}
.border-bottom-1px:after,
.border-bottom-1px:before,
.border-top-1px:after,
.border-top-1px:before {
content: "";
display: block;
position: absolute;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
.border-bottom-1px:after {
border-bottom: 1px solid #ebebeb;
left: 0;
bottom: 0;
width: 100%;
-webkit-transform-origin: 0 bottom;
transform-origin: 0 bottom;
}
.mf-picker .mf-picker-panel .mf-picker-choose .cancel {
left: 0;
}
.mf-picker .mf-picker-panel .mf-picker-choose .confirm {
right: 0;
color: #fc9153;
}
.mf-picker .mf-picker-panel .mf-picker-choose .mf-picker-title {
margin: 0;
line-height: 60px;
font-weight: 400;
text-align: center;
font-size: 18px;
color: #333;
}
.mf-picker .mf-picker-panel .mf-picker-choose .cancel,
.mf-picker .mf-picker-panel .mf-picker-choose .confirm {
position: absolute;
top: 6px;
padding: 16px;
font-size: 14px;
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-top {
position: absolute;
top: 0;
background: -webkit-linear-gradient(bottom, hsla(0, 0%, 100%, .4), hsla(0, 0%, 100%, .8));
background: linear-gradient(bottom, hsla(0, 0%, 100%, .4), hsla(0, 0%, 100%, .8));
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-bottom {
position: absolute;
bottom: 1px;
background: -webkit-linear-gradient(top, hsla(0, 0%, 100%, .4), hsla(0, 0%, 100%, .8));
background: linear-gradient(top, hsla(0, 0%, 100%, .4), hsla(0, 0%, 100%, .8));
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-bottom,
.mf-picker .mf-picker-panel .mf-picker-content .mask-top {
z-index: 10;
width: 100%;
height: 68px;
pointer-events: none;
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
.border-top-1px:before {
border-top: 1px solid #ebebeb;
left: 0;
top: 0;
width: 100%;
}
.mf-picker .mf-picker-panel .wheel-wrapper {
display: -webkit-box;
display: flex;
padding: 0 16px;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel {
flex: 1;
flex-basis: 1e-9px;
width: 1%;
height: 173px;
overflow: hidden;
font-size: 20px;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel:first-child {
flex: 1.8;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .wheel-scroll {
padding: 0;
margin-top: 68px;
line-height: 36px;
list-style: none;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .diff {
transition-duration: 150ms;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .wheel-scroll .wheel-item {
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="css.css">
</head>
<body>
<div class="mf-picker">
<div class="mf-picker-panel">
<div class="mf-picker-choose border-bottom-1px">
<span class="cancel">取消</span>
<span class="confirm">确定</span>
<h1 class="mf-picker-title">选择时间</h1>
</div>
<div class="mf-picker-content">
<div class="mask-top border-bottom-1px"></div>
<div class="mask-bottom border-top-1px"></div>
<div class="wheel-wrapper">
<div class="wheel">
<ul class="wheel-scroll diff">
</ul>
</div>
<div class="wheel">
<ul class="wheel-scroll">
</ul>
</div>
<div class="wheel">
<ul class="wheel-scroll">
</ul>
</div>
</div>
</div>
<div class="mf-picker-footer"></div>
</div>
</div>
</body>
<script src="time.js"></script>
</html>
/**
* [datePicker plugin]
* IOS风格日期选择器,仿滴滴打车预约用车时间选择器
* @Author jawil
* @date 2017-02-17
* @param {[string]} timeStr [返回的时间字符串存在sessionStorage里面]
* @param {[long]} timeStamp [返回的时间戳存在sessionStorage里面]
* @get {[type]} var timeStr= sessionStorage.getItem('timeStr');
* @get {[type]} var timeStamp= sessionStorage.getItem('timeStamp');
*/
'use strict'
//默认配置
const DEFAULT_OPTIONS = {
appointDays: 7, //默认可以预约未来7天
preTime: 20, //默认只能预约10分钟之后,如果两个小时就填120
disMinute: 1 //分钟的间隔,默认一分钟
}
function datePicker(options = {}) {
//最终的配置
const
CHOICE_OPTIONS = Object.assign({}, options, DEFAULT_OPTIONS),
app = CHOICE_OPTIONS.appointDays,
pre_min = CHOICE_OPTIONS.preTime % 60,
dism = CHOICE_OPTIONS.disMinute,
pre_hour = Math.floor(DEFAULT_OPTIONS.preTime / 60)
let daysArr = [],
hoursArr = [],
minutesArr = [],
//用户最终选择日期
selectedYear = '',
selectedDay = '',
selectedHour = '',
selectedMinute = '',
//初始化的时间
initHour, initMinute,
initHourArr = [],
initMinuteArr = [],
isToday = true,
//初始化日期,获得当前日期
date = new Date(),
currentYear = date.getFullYear(),
currentMonth = date.getMonth() + 1,
currentDay = date.getDate(),
currentHours = date.getHours() + pre_hour,
currentMinutes = date.getMinutes()
//筛选符合条件的日期
const filterDate = (f => {
//获取当前月有多少天
let DayNumOfMonth = new Date(currentYear, currentMonth, 0).getDate(),
//获取daysArr
remainDay = DayNumOfMonth - currentDay,
timeStamp = Date.now()
for (let i = 0; i < app; i++) {
let preStamp = timeStamp + 24 * 60 * 60 * 1000 * i,
date = new Date(preStamp),
preYear = date.getFullYear(),
preMonth = date.getMonth() + 1,
preDay = date.getDate()
switch (i) {
case 0:
daysArr.push(`今天(${preMonth}月${preDay}日)`)
break
case 1:
daysArr.push(`明天(${preMonth}月${preDay}日)`)
break
case 2:
daysArr.push(`后天(${preMonth}月${preDay}日)`)
break
default:
daysArr.push(`${preMonth}月${preDay}日`)
break
}
}
//如果是今天的23:30以后预约车,那么今天的就不能预约
if (currentHours == 23 && currentMinutes >= 60 - pre_min) {
daysArr.shift()
}
for (let i = currentHours; i < 24; i++) {
hoursArr.push(i)
initHourArr.push(i)
}
//如果当前的分钟超过pre_min(假设pre_min=30),则小时只能从下一个小时选择,当前时间3:40=>4:10
if (currentMinutes + pre_min > 60 - dism) {
hoursArr.shift()
initHourArr.shift()
}
//如果hoursArr没有数据,说明今天已经不能预约,明天任何小时都可以预约
if (!hoursArr.length) {
for (let h = 0; h < 24; h++) {
hoursArr.push(h)
initHourArr.push(h)
}
}
for (let j = Math.ceil(currentMinutes / dism) * dism + pre_min; j < 60; j += dism) {
minutesArr.push(j)
initMinuteArr.push(j)
}
//如果分钟没有满足条件的,说明现在已经30分以后,小时会自动加1
if (!minutesArr.length) {
for (let k = Math.ceil((currentMinutes + pre_min - 60) / dism) * dism; k < 60; k += dism) {
minutesArr.push(k)
initMinuteArr.push(k)
}
}
})()
//初始化数据
const initData = (f => {
selectedDay = daysArr[0]
selectedHour = initHour = initHourArr[0]
selectedMinute = initMinute = initMinuteArr[0]
})()
let wheel = document.querySelectorAll('.wheel-scroll'),
wheelDay = wheel[0],
wheelHour = wheel[1],
wheelMinute = wheel[2]
//初始化html结构
const initHtml = (f => {
let wheelDayHtml = '',
wheelHourHtml = '',
wheelMinuteHtml = ''
daysArr.forEach(ele => {
wheelDayHtml += `<li class="wheel-item">${ele}</li>`
})
hoursArr.forEach(ele => {
wheelHourHtml += `<li class="wheel-item">${ele}点</li>`
})
minutesArr.forEach(ele => {
wheelMinuteHtml += `<li class="wheel-item">${ele}分</li>`
})
wheelDay.innerHTML = wheelDayHtml
wheelHour.innerHTML = wheelHourHtml
wheelMinute.innerHTML = wheelMinuteHtml
})()
//仿IOS日期风格选择器
class datePicker {
constructor(obj, initIndex = 0, callback) {
this.obj = obj
this.index = -initIndex
this.callback = callback
this.deg = 25 //初始化偏转的角度
this.length = this.obj.children.length
this.distance = this.obj.children[0].offsetHeight
this.ready()
}
static setTranslate3d(obj, dis) {
obj.style.transform = `translate3d(0,${dis}px,0)`
}
static setRotateX(obj, index, deg = 25) {
//设置每个Li的偏转角度
Array.from(obj).forEach((ele, i) => {
obj[i].style.transform = `rotateX(${(i+index)*deg}deg)`
})
}
ready() {
//初始化运动距离
datePicker.setTranslate3d(this.obj, this.index * this.distance)
//初始化3D偏转角度
datePicker.setRotateX(this.obj.children, this.index)
this.bind(this.obj)
}
bind(selector) {
let iStartPageY = 0,
step = 1, //弹性系数
prevPoint = 0,
speed = 0, //手指离开时候的瞬时速度,速度越大,最后停留的越远
timer = null,
length = this.length - 1
const touchstart = e => {
e.preventDefault()
e.stopPropagation()
clearInterval(timer)
iStartPageY = e.changedTouches[0].clientY
prevPoint = iStartPageY
}
const touchmove = e => {
e.preventDefault()
e.stopPropagation()
let iDisY = (e.changedTouches[0].pageY - iStartPageY)
speed = (e.changedTouches[0].pageY - prevPoint)
prevPoint = e.changedTouches[0].pageY
//已滑动在头部或尾部,但是用户还想往上或下滑,这是给一种越往上或下滑越难拖动的体验
if ((this.index == 0 && iDisY > 0) || (this.index == -length && iDisY < 0)) {
step = 1 - Math.abs(iDisY) / selector.clientWidth //根据超出长度计算系数大小,超出的越到 系数越小
step = Math.max(step, 0) //系数最小值为0
iDisY = parseInt(iDisY * step)
}
datePicker.setTranslate3d(selector, this.index * this.distance + iDisY)
datePicker.setRotateX(this.obj.children, this.index + iDisY / this.distance)
}
const touchend = e => {
e.preventDefault()
e.stopPropagation()
let iDisX = e.changedTouches[0].pageY - iStartPageY
//初速度很小的逻辑处理
let flag = false
if (Math.abs(speed) <= 1) {
flag = true
}
timer = setInterval(f => {
if (Math.abs(speed) <= 1) {
clearInterval(timer)
if (flag) {
this.index += Math.round(iDisX / this.distance)
}
this.index = this.index > 0 ? Math.min(this.index, 0) : Math.max(this.index, -length)
this.index = this.index > 0 ? 0 : (this.index < -length ? -length : this.index)
selector.style.transitionDuration = '400ms'
selector.addEventListener("webkitTransitionEnd", f => {
//touchend事件触发后会有一个动画,触发完成后立即清除transition
selector.style.transitionDuration = '0ms'
})
Array.from(selector.children).forEach(ele => {
ele.style.transitionDuration = '200ms'
ele.addEventListener("webkitTransitionEnd", f => {
ele.style.transitionDuration = '0ms'
})
})
datePicker.setTranslate3d(selector, this.index * this.distance)
datePicker.setRotateX(this.obj.children, this.index)
this.callback && this.callback(Math.abs(this.index))
} else {
speed *= 0.2
iDisX += speed
this.index += Math.round(iDisX / this.distance)
}
}, 13);
}
selector.addEventListener("touchstart", touchstart, false)
selector.addEventListener("touchmove", touchmove, false)
selector.addEventListener("touchend", touchend, false)
}
}
new datePicker(wheelDay, 0, indexDay => {
let wheelHourHtml = '',
wheelMinuteHtml = ''
//没有逗号,这个selectedDay是全局变量。。。
selectedDay = daysArr[indexDay]
//今天
if (indexDay == 0) {
isToday = true
//用户选择今天,但是此时的小时已不满足要求,小于当前时间,需要重置初始化小时选项
hoursArr = initHourArr
hoursArr.forEach(ele => {
wheelHourHtml += `<li class="wheel-item">${ele}点</li>`
})
wheelHour.innerHTML = wheelHourHtml
let hindex = selectedHour < initHour ? 0 : hoursArr.indexOf(selectedHour)
//重置当前选择的时间,从明天滑回选择今天需要重置selectedHour
selectedHour = hoursArr[hindex]
new datePicker(wheelHour, hindex, indexHour => {
selectedHour = hoursArr[indexHour]
})
//用户选择今天,但是此时的分钟已不满足要求,小于当前时间,需要重置初始化分钟选项
if (hindex === 0) {
minutesArr = initMinuteArr
minutesArr.forEach(ele => {
wheelMinuteHtml += `<li class="wheel-item">${ele}分</li>`
});
wheelMinute.innerHTML = wheelMinuteHtml
let mindex = selectedMinute < initMinute ? 0 : minutesArr.indexOf(selectedMinute)
//重置当前选择的时间,从明天滑回选择今天需要重置selectedMinute
selectedMinute = minutesArr[mindex]
new datePicker(wheelMinute, mindex, indexMinute => {
selectedMinute = minutesArr[indexMinute]
})
}
//明天或者后天
} else {
//天数选择影响小时
isToday = false
hoursArr = []
for (let h = 0; h < 24; h++) {
hoursArr.push(h)
}
let hindex = hoursArr.indexOf(selectedHour)
hoursArr.forEach((ele) => {
wheelHourHtml += `<li class="wheel-item">${ele}点</li>`
})
wheelHour.innerHTML = wheelHourHtml
new datePicker(wheelHour, hindex, indexHour => {
selectedHour = hoursArr[indexHour]
})
//天数选择影响分钟
minutesArr = [];
for (let m = 0; m < 60; m += dism) {
minutesArr.push(m)
}
let mindex = minutesArr.indexOf(selectedMinute)
wheelMinuteHtml = ''
minutesArr.forEach((ele) => {
wheelMinuteHtml += `<li class="wheel-item">${ele}分</li>`;
})
wheelMinute.innerHTML = wheelMinuteHtml
new datePicker(wheelMinute, mindex, indexMinute => {
selectedMinute = minutesArr[indexMinute];
})
}
})
new datePicker(wheelHour, 0, indexHour => {
let wheelMinuteHtml = ''
selectedHour = hoursArr[indexHour]
//滑到头部,这是要处理分钟是否小于当前时间
if (indexHour == 0 && isToday) {
minutesArr = initMinuteArr
minutesArr.forEach(ele => {
wheelMinuteHtml += `<li class="wheel-item">${ele}分</li>`
});
wheelMinute.innerHTML = wheelMinuteHtml
let mindex = selectedMinute < initMinute ? 0 : minutesArr.indexOf(selectedMinute)
//重置当前选择的时间,从明天滑回选择今天需要重置selectedMinute
selectedMinute = minutesArr[mindex]
new datePicker(wheelMinute, mindex, indexMinute => {
selectedMinute = minutesArr[indexMinute]
})
} else {
minutesArr = []
for (let m = 0; m < 60; m += dism) {
minutesArr.push(m)
}
let mindex = minutesArr.indexOf(selectedMinute)
minutesArr.forEach(ele => {
wheelMinuteHtml += `<li class="wheel-item">${ele}分</li>`
});
wheelMinute.innerHTML = wheelMinuteHtml
new datePicker(wheelMinute, mindex, indexMinute => {
selectedMinute = minutesArr[indexMinute]
})
}
})
new datePicker(wheelMinute, 0, indexMinute => {
selectedMinute = minutesArr[indexMinute]
})
//获得最后选择的日期
const confirmTime = e => {
e.preventDefault()
e.stopPropagation()
const minute = selectedMinute,
hour = selectedHour,
day = parseInt(selectedDay.split('月')[1]),
month = parseInt(selectedDay.split('月')[0].slice(-1)),
year = (month == 1 && day < app) ? currentYear + 1 : currentYear
//IOS版本浏览器不兼容new Date('2017-04-11')这种格式化,故用new Date('2017/04/11')
let timeStamp = new Date(`${year}/${month}/${day} ${hour}:${minute}`).getTime(),
timeStr = `${selectedDay} ${hour}点${minute}分`
sessionStorage.setItem('timeStamp', timeStamp)
sessionStorage.setItem('timeStr', timeStr)
console.log(year, month, day, hour, minute, timeStamp, timeStr)
document.querySelector('.mf-picker').style.display = 'none'
}
//显示隐藏
const toggle = e => {
e.preventDefault()
e.stopPropagation()
document.querySelector('.mf-picker').style.display = 'none'
}
document.querySelector('.confirm').addEventListener('touchend', confirmTime, false)
document.querySelector('.cancel').addEventListener('touchend', toggle, false)
document.querySelector('.mf-picker').addEventListener('touchend', toggle, false)
}
datePicker({
appointDays: 7, //默认可以预约未来7天
preTime: 20, //默认只能预约10分钟之后,如果两个小时就填120
disMinute: 1 //分钟的间隔,默认一分钟
})
时间紧迫,VUE 版本的请评论留言。我会回复的。
vue 的
<template>
<div class="mf-picker">
<div class="mf-picker-panel">
<div class="mf-picker-choose border-bottom-1px">
<span class="nowTime" @click="nowTime()">现在出发</span>
<span class="confirm" @click="confirm()">确定</span>
<h1 class="mf-picker-title">选择出发时间</h1>
</div>
<div class="mf-picker-content">
<div class="mask-top border-bottom-1px"></div>
<div class="mask-bottom border-top-1px"></div>
<div class="wheel-wrapper">
<div class="wheel">
<ul class="wheel-scroll diff">
</ul>
</div>
<div class="wheel">
<ul class="wheel-scroll">
</ul>
</div>
<div class="wheel">
<ul class="wheel-scroll">
</ul>
</div>
</div>
</div>
<div class="mf-picker-footer"></div>
</div>
</div>
</template>
<script>
import Vue from "vue";
import {datePicker, confirmTime} from "../timePicker.js";
export default {
name: "mf-picker",
data: () => ({}),
mounted() {
datePicker({
appointDays: 7, //默认可以预约未来7天
preTime: 20, //默认只能预约10分钟之后,如果两个小时就填120
disMinute: 5 //分钟的间隔,默认一分钟
});
},
methods: {
confirm() {
confirmTime();
},
nowTime() {
console.log(1111)
}
}
};
</script>
<style scoped>
.mf-picker .mf-picker-panel {
position: absolute;
z-index: 66666;
bottom: 0;
width: 100%;
height: 273px;
background: #fff;
box-shadow:0px -2px 4px 0px rgba(0,0,0,0.1);
border-radius:10px 10px 0px 0px;
}
.mf-picker .mf-picker-panel .mf-picker-choose {
position: relative;
height: 60px;
color: #999;
}
.mf-picker .mf-picker-panel .mf-picker-content {
position: relative;
top: 20px;
height: 173px;
}
.border-bottom-1px,
.border-top-1px {
position: relative;
}
.border-bottom-1px:after,
.border-bottom-1px:before,
.border-top-1px:after,
.border-top-1px:before {
content: "";
display: block;
position: absolute;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
.border-bottom-1px:after {
border-bottom: 1px solid #ebebeb;
left: 0;
bottom: 0;
width: 100%;
-webkit-transform-origin: 0 bottom;
transform-origin: 0 bottom;
}
.mf-picker .mf-picker-panel .mf-picker-choose .nowTime {
left: 0;
}
.mf-picker .mf-picker-panel .mf-picker-choose .confirm {
right: 0;
color: #fc9153;
}
.mf-picker .mf-picker-panel .mf-picker-choose .mf-picker-title {
margin: 0;
line-height: 60px;
font-weight: bold;
text-align: center;
font-size: 18px;
color: #333;
}
.mf-picker .mf-picker-panel .mf-picker-choose .nowTime,
.mf-picker .mf-picker-panel .mf-picker-choose .confirm {
position: absolute;
top: 0;
padding: 16px;
font-size: 15px;
}
.mf-picker .mf-picker-panel .mf-picker-choose .confirm {
height: 30px;
line-height: 30px;
background: rgba(37,163,255,1);
border-radius: 2px;
padding: 0;
display: inline-block;
width: 56px;
margin: 15px;
color: #fff;
}
.mf-picker .mf-picker-panel .mf-picker-choose .nowTime {
height: 30px;
line-height: 30px;
border-radius: 2px;
padding: 0 7px;
font-size: 14px;
display: inline-block;
margin: 15px;
height: 30px;
border-radius: 2px;
border: 1px solid rgba(192,192,192,1);
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-top {
position: absolute;
top: 0;
background: -webkit-linear-gradient(
bottom,
hsla(0, 0%, 100%, 0.4),
hsla(0, 0%, 100%, 0.8)
);
background: linear-gradient(
bottom,
hsla(0, 0%, 100%, 0.4),
hsla(0, 0%, 100%, 0.8)
);
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-bottom {
position: absolute;
bottom: 1px;
background: -webkit-linear-gradient(
top,
hsla(0, 0%, 100%, 0.4),
hsla(0, 0%, 100%, 0.8)
);
background: linear-gradient(
top,
hsla(0, 0%, 100%, 0.4),
hsla(0, 0%, 100%, 0.8)
);
}
.mf-picker .mf-picker-panel .mf-picker-content .mask-bottom,
.mf-picker .mf-picker-panel .mf-picker-content .mask-top {
z-index: 10;
width: 100%;
height: 68px;
pointer-events: none;
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
.border-top-1px:before {
border-top: 1px solid #ebebeb;
left: 0;
top: 0;
width: 100%;
}
.mf-picker .mf-picker-panel .wheel-wrapper {
display: -webkit-box;
display: flex;
padding: 0 16px;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel {
flex: 1;
flex-basis: 1e-9px;
width: 1%;
height: 173px;
overflow: hidden;
font-size: 16px;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel:first-child {
flex: 1.8;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .wheel-scroll {
padding: 0;
margin-top: 68px;
line-height: 36px;
list-style: none;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .diff {
transition-duration: 150ms;
}
.mf-picker .mf-picker-panel .wheel-wrapper .wheel .wheel-scroll .wheel-item {
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
</style>
timePicker.js 下载
链接:https://pan.baidu.com/s/1aAIJGityXdCH5mryp7t3jw
提取码:2349