css 代码 * {
margin: 0;
padding: 0;
}
ul,
li {
list-style: none;
}
.box {
height: 300px;
width: 400px;
margin: 50px auto;
border: 1px solid #000;
border-radius: 12px;
position: relative;
touch-action: none;
}
.box .columns {
overflow: hidden;
height: 100%;
}
.box .columns .options {
transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
transition-duration: 0ms;
transition-property: all;
}
.box .columns .options li {
height: 60px;
line-height: 60px;
text-align: center;
font-weight: bold;
}
.box .columns .mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 12px;
z-index: 1;
pointer-events: none;
background-repeat: no-repeat;
background-position: top, bottom;
-webkit-transform: translateZ(0);
transform: translateZ(0);
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.9) 0%,
rgba(255, 255, 255, 0.1) 45%,
rgba(255, 255, 255, 0.1) 55%,
rgba(255, 255, 255, 0.9) 100%
);
}
.box .columns .hairline {
border-bottom: 1px solid #000;
position: absolute;
top: 50%;
right: 16px;
left: 16px;
height: 60px;
transform: translateY(-50%);
z-index: 2;
pointer-events: none;
}
javascript 代码const boxEl = document.querySelector('.box')
const columnsEl = boxEl.querySelector('.columns')
const optionsEl = columnsEl.querySelector('.options')
const liFirstEl = optionsEl.querySelector('li:first-child')
const boxHeight = boxEl.clientHeight
const liHeight = liFirstEl.offsetHeight
const liAllEl = optionsEl.querySelectorAll('li')
const liCount = liAllEl.length
let startY = 0
let endY = 0
let currentY = 0
let isTouch = false
const initPositionY = boxHeight / 2 - liHeight / 2
let beginPositionY = initPositionY
const maxTranslateY = initPositionY + liHeight
const minTranslateY = boxHeight / 2 - liCount * liHeight
;(function () {
optionsEl.setAttribute(
'style',
`transition-duration: 0ms; transition-property: all;`
)
let index = -1
for (let i = 0; i < liCount; i++) {
if (!liAllEl[i].hasAttribute('disabled')) {
index = i
break
}
}
if (index > -1) {
liAllEl[index].classList.add('selected')
beginPositionY = initPositionY - index * liHeight
setTransform(beginPositionY, optionsEl)
} else {
console.log('没有可用的选项')
beginPositionY = boxHeight / 2 + liHeight / 2
setTransform(beginPositionY, optionsEl)
}
})()
function setTransform(value, el) {
el.style.transform = `translate3d(0px, ${value}px, 0px)`
}
function getTranslateY(el) {
const translateStr = el.style.transform
const valueStr = translateStr.match(/\(([^)]*)\)/)
const valueArr = valueStr[1].split(',')
return Number(valueArr[1].replace('px', ''))
}
function setTransitionDuration(value, el) {
el.style.transitionDuration = `${value}ms`
el.style.transitionProperty = value === 0 ? 'none' : 'all'
}
function setTransitionNone(el) {
setTimeout(() => {
setTransitionDuration(0, el)
}, 300)
}
function setClassName(index, elList) {
for (let i = 0; i < elList.length; i++) {
if (i === index) {
elList[i].classList.add('selected')
} else {
elList[i].classList.remove('selected')
}
}
}
columnsEl.addEventListener(
'touchmove',
function (e) {
e.preventDefault()
},
false
)
columnsEl.addEventListener(
'click',
function (e) {
console.log('click 事件触发')
const target = e.target
let index = 0
index = target.dataset.index
if (liAllEl[index].hasAttribute('disabled')) {
return false
}
beginPositionY = initPositionY - index * liHeight
setTransitionDuration(200, optionsEl)
setTransform(beginPositionY, optionsEl)
setTransitionNone(optionsEl)
setClassName(index, liAllEl)
},
false
)
optionsEl.addEventListener(
'touchstart',
function (e) {
console.log('touchstart 事件触发')
startY = event.targetTouches[0].pageY
},
false
)
optionsEl.addEventListener(
'touchmove',
function (e) {
endY = event.targetTouches[0].pageY
let moveY = endY - startY
if (moveY !== 0) {
isTouch = true
}
console.log('touchmove 事件触发')
currentY = beginPositionY + moveY
if (currentY > maxTranslateY) {
currentY = maxTranslateY
} else if (currentY < minTranslateY) {
currentY = minTranslateY
}
setTransform(currentY, optionsEl)
},
false
)
function touchend() {
if (!isTouch) {
return false
}
console.log('touchend 事件触发')
let index
setTransitionDuration(200, optionsEl)
const isDragDown = endY - startY > 0
if (currentY >= maxTranslateY) {
currentY = currentY - liHeight
index = 0
} else if (currentY <= minTranslateY) {
currentY = currentY + liHeight / 2
index = liCount - 1
} else {
if (isDragDown) {
index = Math.floor((initPositionY - currentY) / liHeight)
} else {
index = Math.ceil((initPositionY - currentY) / liHeight)
}
index = index <= 0 ? 0 : index >= liCount - 1 ? liCount - 1 : index
currentY = initPositionY - liHeight * index
}
if (liAllEl[index].hasAttribute('disabled')) {
let optionalIndex = index
if (isDragDown) {
for (let i = index; i >= 0; i--) {
if (!liAllEl[i].hasAttribute('disabled')) {
optionalIndex = i
break
}
}
} else {
for (let i = index; i < liCount; i++) {
if (!liAllEl[i].hasAttribute('disabled')) {
optionalIndex = i
break
}
}
}
if (index !== optionalIndex) {
console.log('optionalIndex------' + optionalIndex)
index =
optionalIndex <= 0
? 0
: optionalIndex >= liCount - 1
? liCount - 1
: optionalIndex
currentY = initPositionY - liHeight * index
} else {
index = index <= 0 ? 0 : index >= liCount - 1 ? liCount - 1 : index
currentY = beginPositionY
}
}
setTransform(currentY, optionsEl)
beginPositionY = currentY
setClassName(index, liAllEl)
isTouch = false
setTransitionNone(optionsEl)
}
optionsEl.addEventListener('touchend', touchend, false)
optionsEl.addEventListener(
'touchcancel',
(evt) => {
evt.preventDefault()
console.log('touchcancel 事件触发')
touchend()
},
false
)