看到网上很多关于Vue的尺子组件,有一些上传到npm官网的,但是使用并不方便,支持的定制化也不够用,抽空写了一个,总体来说主要是样式写好,效果是基于better-scroll实现的,支持bounce效果,自动识别最近的刻度,只需要传入自己的最大值和最小值,就会帮你算出一个包含最大值的最大刻度,整体效果不错,由于是demo,没有基于任何UI效果,要达到自己设计师要求的UI效果,可以将这份代码拷贝过去自己改造定义一个尺子的Vue组件来使用,效果图:
页面DOM结构如下,样式采用的stylus预处理语法,个人觉得stylus就是为css而生的,相比less,sass好用很多,请现在webpack.config.js里面配上处理stylus的规则,
<template>
<div class="box">
<div class="arrow">{{value}}</div>
<div class="wrapper ruler">
<ul ref="ul" :style="{width: ul_width + 'px'}">
<li v-for="(v, i) in ruler_max_num" :key="i">
<label>{{v * 10}}</label>
<span v-for="(e, d) in 10" :key="d"></span>
</li>
</ul>
</div>
</div>
</template>
样式如下:
<style lang="stylus" rel="stylesheet/stylus" scoped>
.box
.arrow
font-weight 500
width 100%
height 20px
text-align center
position relative
top -20px
&::after
content ''
position absolute
left 50%
top 110px
transform translateX(-50%)
border 8px solid black
border-color black transparent transparent transparent
.ruler
font-size 12px
width 100%
padding-top 100px
>ul
width 100%
height 80px
border 1px solid #ddd
border-left none
border-right none
display flex
justify-content space-between
background #fff
>li
height 18px
display flex
justify-content space-between
box-sizing border-box
position relative
&:first-child
position relative
&::before
position absolute
content ""
left -1px
top 0
height 11px
width 1px
background black
opacity 0.8
&::after
position absolute
content ""
right 0
top 0
height 15px
width 1px
background black
>label
position absolute
right -6px
bottom -16px
>span
width 10px
height 11px
box-sizing border-box
border-right 1px solid black
opacity 0.8
position relative
&::after
position absolute
content ""
left 50%
top 0
transform translateX(-50%)
height 8px
width 1px
background black
opacity 0.5
</style>
js代码如下:
<script>
import BScroll from 'better-scroll'
export default {
props: {
max_val: { // 需要
type: Number,
default() {
return 168
}
},
min_val: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
split_val: 10,
value: 0
}
},
mounted() {
let ul = this.$refs.ul
let lis = ul.getElementsByTagName('li')
let first_li = lis[0]
let last_li = lis[lis.length - 1]
first_li.style.marginLeft = document.body.clientWidth/2 + 'px'
last_li.style.marginRight= document.body.clientWidth/2 + 'px'
this.$nextTick(()=>{
let bs = new BScroll('.wrapper', {
startX: 0,
scrollX: true,
probeType: 2,
useTransition: false,
bounce: true,
momentum: true
})
bs.on('scrollEnd', ({x, y}) => {
if (x > 0) {
x = 0
}
this.value = -Math.round(x/10)
bs.scrollTo(Math.round(x/10) * 10, 0)
})
})
},
computed: {
ul_width() {
return this.ruler_max_num * 10 * 10 + document.body.clientWidth
},
ruler_max_num() {
let split_num = Math.floor(this.max_val / this.split_val)
let left_v = this.max_val % this.split_val
if (left_v > 0) {
split_num = split_num + 1
}
return split_num
}
}
}
</script>
基本上项目安装好better-scroll后就可以运行起来了,
今天做了一个小小的优化,支持传入最大值和最小值,从而只显示这个值域段的刻度
样式添加:
<style lang="stylus" rel="stylesheet/stylus" scoped>
.box
.arrow
font-weight 500
width 100%
height 20px
text-align center
position relative
top -20px
&::after
content ''
position absolute
left 50%
top 110px
transform translateX(-50%)
border 8px solid black
border-color black transparent transparent transparent
.ruler
font-size 12px
width 100%
padding-top 100px
>ul
width 100%
height 80px
border 1px solid #ddd
border-left none
border-right none
display flex
justify-content space-between
background #fff
>li
height 18px
display flex
justify-content space-between
box-sizing border-box
position relative
&:first-child
position relative
&::before
position absolute
content ""
left -1px
top 0
height 11px
width 1px
background black
opacity 0.8
&::after
position absolute
content ""
right 0
top 0
height 15px
width 1px
background black
>label
position absolute
right -6px
bottom -16px
&.first
right 100%
bottom -14px
margin-right -6px
>span
width 10px
height 11px
box-sizing border-box
border-right 1px solid black
opacity 0.8
position relative
&::after
position absolute
content ""
left 50%
top 0
transform translateX(-50%)
height 8px
width 1px
background black
opacity 0.5
</style>
JS逻辑:
export default {
props: {
max_val: {
type: Number,
default() {
return 888
}
},
min_val: {
type: Number,
default() {
return 666
}
}
},
data() {
return {
split_val: 10,
value: 0,
initail_val: 0
}
},
mounted() {
let ul = this.$refs.ul
let lis = ul.getElementsByTagName('li')
let first_li = lis[0]
let last_li = lis[lis.length - 1]
first_li.style.marginLeft = document.body.clientWidth/2 + 'px'
last_li.style.marginRight= document.body.clientWidth/2 + 'px'
this.value = this.ruler_min_num * 10
this.initail_val = this.ruler_min_num * 10
this.$nextTick(()=>{
let bs = new BScroll('.wrapper', {
startX: 0,
scrollX: true,
probeType: 2,
useTransition: false,
bounce: true,
momentum: true
})
bs.on('scrollEnd', ({x, y}) => {
if (x > 0) {
x = 0
}
this.value = -Math.round(x / 10) + this.ruler_min_num * 10
bs.scrollTo(Math.round(x / 10) * 10, 0)
})
})
},
computed: {
ul_width() {
return this.actual_num * 10 * 10 + document.body.clientWidth
},
ruler_max_num() {
let split_num = Math.floor(this.max_val / this.split_val)
let left_v = this.max_val % this.split_val
if (left_v > 0) {
split_num = split_num + 1
}
return split_num
},
ruler_min_num() {
let split_num = Math.floor(this.min_val / this.split_val)
return split_num
},
actual_num() {
return this.ruler_max_num - this.ruler_min_num
}
}
}
效果图:传入最大值88,传入最小值66,显示为60-90之前的尺段,提高尺子的滑动效率: