小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
TIP 👉 冤家宜解不宜结,各自回头看后头。明·冯梦龙《古今小说》
前言
在我们日常项目开发中,我们在做移动端的时候会涉及到滚动选择功能,所以封装了这个滚动选择组件。滚动选择组件
属性
1. value
- 当前选中值,可以为options中选项对象,也可以为选项的value
- 值类型为:字符串(选项value)、数值(选项value)或对象(选项对象)
2. options
- 选项数组
- 值类型为数组,示例:[{ value: '01', text: '男'}, { value: '02', text: '女'}]
事件
1. change
- 值发生改变时触发的事件
- 参数:
- currentOption:当前选中选项对象,例:{ value: '01', text: '男'}
2. input
- 值发生改变时触发的事件
- 参数:
- value:当前选中选项的value,示例:'01'
实现scrollSelect.vue
<template>
<div>
<div class="f-flex f-flext scroll-select" @touchmove.prevent="" @mousewheel.prevent="">
<div class="f-flex1 f-tac f-oh">
<ul :class="{'dragging': dragging}" @touchstart.stop="handleTouchStart($event)" @mousedown.stop="handleTouchStart($event)" :style="{'transform' : 'translate3d(0,' + translateY + 'px, 0)'}">
<li ref="li"></li>
<li class="f-toe" v-for="(item, index) in options" :key="item.value" :class="{
'current': currentOption ? item.value === currentOption.value : false,
'node1': Math.abs(index - currentIndex) == 1,
'node2': Math.abs(index - currentIndex) == 2,
'node3': Math.abs(index - currentIndex) >= 3
}">{{item.text}}</li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div class="net-monitor"></div>
</div>
</div>
</template>
<script>
export default {
name: 'ScrollSelect',
props: {
// 当前选中值
value: [String, Number, Object],
/**
* 选择项列表数组
* 数组中对象示例:{ value: '01', text: '男'}
* value: 值
* text: 显示的文字
**/
options: {
type: Array,
default: () => []
}
},
data () {
return {
// 当前选中值的索引位置
currentIndex: 0,
// 当前选中值的选项信息,例:{ value: '01', text: '男'}
currentOption: {},
dragging: false,
distanceY: 0,
translateY: 0
}
},
watch: {
value (val) {
this.initValue(val)
this.initTranslateY()
},
options (val) {
if (val && val instanceof Array) {
this.initValue(this.currentOption)
this.initTranslateY()
}
}
},
created () {
this.initValue(this.value)
},
mounted () {
this.initTranslateY()
if (typeof this.value === 'string' || typeof this.value === 'number') {
this.$emit('change', this.currentOption)
}
},
beforeDestroy () {
document.removeEventListener('touchmove', this.handleTouchMove)
document.removeEventListener('touchend', this.handleTouchEnd)
document.removeEventListener('mousemove', this.handleTouchMove)
document.removeEventListener('mouseup', this.handleTouchEnd)
},
methods: {
initValue (value) {
let currentIndex = 0
let currentOption = this.options.length > 0 ? this.options[0] : null
if (value !== null && typeof value !== 'undefined') {
this.options.forEach((val, idx) => {
if (typeof value === 'string' || typeof value === 'number') {
if (val.value === value) {
currentOption = val
currentIndex = idx
}
} else if (val.value === value.value) {
currentOption = val
currentIndex = idx
}
})
}
this.currentIndex = currentIndex
this.currentOption = currentOption
},
initTranslateY () {
let clientHeight = this.$refs.li.offsetHeight
this.translateY = -clientHeight * this.currentIndex
},
setPage () {
let clientHeight = this.$refs.li.offsetHeight
let total = this.options.length
let goPage = Math.round((this.translateY / clientHeight).toFixed(1))
if (goPage > 0) {
goPage = 0
}
goPage = total - 1 >= Math.abs(goPage) ? goPage : -(total - 1)
let index = Math.abs(goPage)
this.currentOption = this.options[index]
this.currentIndex = index
this.translateY = goPage * clientHeight
this.$emit('change', this.currentOption)
this.$emit('input', this.currentOption.value)
},
getPageY (e) {
return e.changedTouches ? e.changedTouches[0]['pageY'] : e['pageY']
},
handleTouchStart (e) {
this.distanceY = 0
this.startY = this.getPageY(e)
this.startTranslateY = this.translateY
this.dragging = true
document.addEventListener('touchmove', this.handleTouchMove, false)
document.addEventListener('touchend', this.handleTouchEnd, false)
document.addEventListener('mousemove', this.handleTouchMove, false)
document.addEventListener('mouseup', this.handleTouchEnd, false)
},
handleTouchMove (e) {
this.distanceY = this.getPageY(e) - this.startY
this.translateY = this.startTranslateY + this.distanceY
},
handleTouchEnd (e) {
this.dragging = false
this.setPage()
document.removeEventListener('touchmove', this.handleTouchMove)
document.removeEventListener('touchend', this.handleTouchEnd)
document.removeEventListener('mousemove', this.handleTouchMove)
document.removeEventListener('mouseup', this.handleTouchEnd)
},
// 获取默认值
getDefaultValue (value) {
if (this.options.length > 0) {
return this.options[0]
} else {
return {}
}
}
}
}
</script>
<style lang="scss" scoped>
.scroll-select {
position: relative;
width: 100%;
margin: 0 auto;
background: transparent;
height: 350px;
overflow: hidden;
color: #2D3859;
ul {
transition: all .4s ease;
padding-top: 27px;
&.dragging {
transition: none;
}
li {
line-height: 98px;
height: 98px;
font-size: 36px;
text-align: center;
color: #a8a8a8;
transition: .3s ease;
&.current {
font-size: 40px;
color: #333333;
}
&.node1 {
font-size: 36px;
opacity: .7;
}
&.node2 {
font-size: 32px;
opacity: .5;
}
&.node3 {
font-size: 28px;
opacity: .3;
}
}
}
}
.net-monitor {
width: 100%;
height: 94px;
border: 1px solid $base-color;
border-left-width: 0;
border-right-width: 0;
@include base-border-color(.5);
position: absolute;
bottom: 125px;
@include base-background-color(.1);
z-index: -1;
}
.f-toe {
overflow: hidden;
word-wrap: normal;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
index.js
/**
* 滚动选择组件
*/
import ScrollSelect from './ScrollSelect.vue'
export default ScrollSelect
「欢迎在评论区讨论」