<template>
<div class="ruler-wrap">
<div class="ruler-box" ref="wrap">
<div class="highlight" :style="{ left: indexBarP(),background:indexColor }"></div>
<div class="scroll">
<div class="ruler" ref="ruler" :style="{width:rulerWidth}"></div>
</div>
<div class="number">
<span v-for="(item, index) in itemCount" :key="index">{{invidatorMin + index * step}}</span>
</div>
<img
src="https://files.lifesense.com/other/20200401/384b6ca2045f4376b214d2159fe91b4b.png"
class="shadow left"
/>
<img
src="https://files.lifesense.com/other/20200401/384b6ca2045f4376b214d2159fe91b4b.png"
class="shadow right"
/>
</div>
</div>
</template>
<script>
let timer;
export default {
name: "Ruler",
props: {
min: {
type: Number,
default: 40,
required: true
},
max: {
type: Number,
default: 220,
required: true
},
value: {
type: Number,
default: 90,
required: true
},
indexColor: {
type: String,
default: "#16c5ef"
},
step: {
type: Number,
default: 10
},
accuracy: {
type: Number,
default: 0
}
},
data() {
return {
rem: 16,
unitW: 8,
indexBarPosition: 0,
currValue: 20,
itemCount: 0,
el: null,
invidatorMin: this.min,
invidatorMax: this.max
};
},
methods: {
indexBarP() {
return (this.indexBarPosition / this.rem + 0.05) + "rem";
},
initScroll() {
let wholeWidth = this.$refs.ruler.offsetWidth;
this.el = this.$refs.wrap;
let unitW = wholeWidth / ((this.itemCount + 5) * 10);
this.unitW = unitW;
this.indexBarPosition = Math.floor(this.el.offsetWidth / 2);
let leftPeak =
25 * unitW -
this.indexBarPosition +
1.5 +
(this.min - this.invidatorMin / this.step) * 10 * this.unitW; //左边峰值,禁止超出
let rightPeak =
((this.max - this.min) / this.step) * 10 * this.unitW + leftPeak;
this.el.onscroll = e => {
let scrollLeft = e.target.scrollLeft;
let value = this.accuracy
? Number(
(((scrollLeft - leftPeak) / this.unitW / 10) * this.step).toFixed(
this.accuracy
)
)
: Math.round(((scrollLeft - leftPeak) / this.unitW / 10) * this.step);
let realValue = this.min + value;
this.currValue =
realValue >= this.max
? this.max
: realValue <= this.min
? this.min
: realValue;
clearTimeout(timer);
let params = {
scrollLeft,
leftPeak,
rightPeak,
unitW,
time: new Date().getTime()
};
timer = setTimeout(this.adjust,100, params);
};
},
adjust(params) {
console.log(params.scrollLeft + ':' +this.el.scrollLeft);
if (params.scrollLeft === this.el.scrollLeft) {
if (params.scrollLeft < params.leftPeak) {
this.el.scrollTo(params.leftPeak, 0);
} else if (params.scrollLeft > params.rightPeak) {
this.el.scrollTo(params.rightPeak, 0);
} else if (
Math.floor(params.scrollLeft) !== Math.floor(params.leftPeak) &&
Math.floor(params.scrollLeft) !== Math.floor(params.rightPeak)
) {
let end = params.scrollLeft;
let canMove = Math.round(end / params.unitW) * params.unitW + 2;
this.el.scrollTo(canMove, 0);
}
}
},
init() {
let wholeWidth = this.$refs.ruler.offsetWidth; //游标卡尺总宽度
this.el = this.$refs.wrap;
let unitW = wholeWidth / ((this.itemCount + 5) * 10); //每个小格子站的宽度
this.unitW = unitW;
//中间索引条位置
this.indexBarPosition = Math.floor(this.el.offsetWidth / 2);
let leftPeak =
25 * unitW -
this.indexBarPosition +
1.5 +
(this.min - this.invidatorMin / this.step) * 10 * this.unitW; //左边峰值,禁止超出
let rightPeak =
((this.max - this.min) / this.step) * 10 * this.unitW + leftPeak;
let initialP =
leftPeak + (((this.value - this.min) * 10) / this.step) * unitW;
this.el.scrollTo(initialP, 0);
this.initScroll();
}
},
created() {
this.invidatorMin = Math.floor(this.min);
this.invidatorMax = Math.ceil(this.max);
this.itemCount =
Math.ceil(this.invidatorMax - this.invidatorMin) / this.step + 1;
this.rem = (document.documentElement.offsetWidth / 375) * 16;
this.currValue = this.value;
},
mounted() {
this.init();
},
watch: {
currValue(value) {
this.$emit("change", value);
}
},
computed: {
rulerWidth() {
return (this.itemCount + 5) * 25 + "vw";
}
}
};
</script>
<style lang="less" scoped>
.ruler-wrap {
position: relative;
}
.ruler-box {
overflow-x: scroll;
height: 72px;
background: linear-gradient(
270deg,
rgba(251, 251, 251, 1) 0%,
rgba(255, 255, 255, 0) 100%
);
&::-webkit-scrollbar {
display: none;
}
}
.number {
white-space: nowrap;
margin-top: -30px;
span {
display: inline-block;
width: 25vw;
height: 12px;
flex: 1;
text-align: center;
color: #999999;
&:first-of-type {
margin-left: 50vw;
}
}
}
.scroll {
position: relative;
width: 100%;
height: 72px;
border-radius: 4px;
.ruler {
height: 36px;
display: flex;
background-image: url('https://files.lifesense.com/other/20210225/e912461b411b45eda1215d081476b3b9.png');
background-position: 12.5vw 0;
background-size: 100vw 37px
}
}
.highlight {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-100%);
width: 0.9vw;
height: 32px;
background-color: #16c5ef;
border-radius: 0 0 2px 2px;
z-index: 1;
}
.shadow {
position: absolute;
height: 100%;
width: 120px;
pointer-events: none;
&.left {
left: 0;
top: 0;
}
&.right {
right: 0;
top: 0;
transform: rotate(180deg);
}
}
</style>
component 滑动尺子 - 丝滑版
于 2022-02-24 11:15:00 首次发布