需求
1、长按按钮+/长按按钮-,输入框数字自增/减少。
2、长按时,鼠标移出了按钮区域,不管原来click的是+,还是-,以鼠标竖直方向的差值绝对值为步长,只要mousemove方向往上,数字增加,方向往下,数字减少。
3、点击输入框可以直接修改输入框的数值。
4、可以输入正负整数。支持设置最大、小值,输入后自动调整数值到区间内。
5、hover按钮/输入框,边框为蓝色。
6、数值达到临界值无法再调整后,hover鼠标按钮样式为禁用状态
input 需求2不知道怎么实现,因此自行实现一个input-number组件。
期望
图为element input-number效果图
技术点
1、mousedown事件、mouseout事件、mouseup事件、setInterval
2、mousemove事件、父级组件调用子组件方法(refs的使用)
3、鼠标方向的判断
4、隐藏input"上下"箭头
交互效果:
5、mouseenter事件、mouseleave事件,border-color
6、cursor: not-allowed;
去掉按钮focus边框 outline: none;
最终效果
参考 VUE长按事件
参考 隐藏input type=number元素,右侧的上下箭头
代码参考
父类调用者 LandingPage.vue
<template>
//省略....
<input-number
:value="1"
:max-num="Number.MAX_VALUE"
:min-num="0"
ref="rightSide_inpunumber"
></input-number>
//省略....
</template>
<script>
import InputNumber from "./Basic/InputNumber";
export default {
name: "landing-page",
components: { SystemInformation, InputNumber },
data() {
return {
oldclientY: -1,
};
},
methods: {
pageMouseUp() {
// console.log("pageMouseUp");
this.$refs.rightSide_inpunumber.buttonMouseup();
},
pageMouseMove(e) {
let offsetY = e.clientY - this.oldclientY;
let flag = 0;
if (offsetY > 0) {
flag = 1;
} else if (offsetY < 0) {
flag = -1;
}
this.oldclientY = e.clientY;
if (offsetY != 0 && this.$refs.rightSide_inpunumber.isClickButton) {
offsetY = Math.abs(offsetY);
this.$refs.rightSide_inpunumber.updateOffset(flag, offsetY);
}
},
},
};
</script>
组件 InputNumber.vue
<template>
<div class="inputNumber-wrapper">
<input
class="numboxInput"
:style="inputStyle"
type="number"
:value="currentVal"
@change="fixNumber"
@mouseenter="inputMouseEnter"
@mouseleave="inputMouseLeave"
/>
<button
id="reduceBtn"
:class="reduceBtnClass"
@mousedown="buttonMousedown(-1)"
@mouseenter="inputMouseEnter"
@mouseleave="inputMouseLeave"
@mouseout="buttonMouseOut"
@mouseup="buttonMouseup()"
>-</button>
<button
id="addBtn"
:class="addBtnClass"
@mousedown="buttonMousedown(1)"
@mouseenter="inputMouseEnter"
@mouseleave="inputMouseLeave"
@mouseout="buttonMouseOut"
@mouseup="buttonMouseup()"
>+</button>
</div>
</template>
<script>
export default {
name: "inputnumber",
data() {
return {
addBtnClass: "btn",
reduceBtnClass: "btn",
inputStyle: {
"border-color": "",
},
timeInterval: null,
currentVal: this.value,
isClickButton: false,
};
},
components: {},
props: {
value: {
type: Number,
default: 10,
},
maxNum: {
type: Number,
default: Infinity,
},
minNum: {
type: Number,
default: -Infinity,
},
step: {
type: Number,
default: 1,
},
},
watch: {
currentVal(val) {
this.$emit("input", val);
},
},
methods: {
/**
* 交互效果 enter-变蓝
*/
inputMouseEnter() {
this.updateInputBorder(true);
this.updateCursor();
},
/**
* 交互效果 leave-恢复
*/
inputMouseLeave() {
this.updateInputBorder(false);
this.updateCursor();
},
/**
* 交互效果 input边框
*/
updateInputBorder(isChanging) {
if (isChanging) {
this.inputStyle["border-color"] = "#409eff";
} else {
this.inputStyle["border-color"] = "";
}
},
fixNumber($event) {
let fix;
if (typeof $event.target.value != "number") {
fix = Number($event.target.value);
} else {
fix = $event.target.value;
}
this.updateVal(fix);
},
updateVal(val) {
if (val > this.maxNum) val = this.maxNum;
if (val < this.minNum) val = this.minNum;
this.currentVal = val;
},
/**
* flag : +/-
* offset :一次加/减的值
*/
updateOffset(flag, offset) {
let isCanSet = false;
if (flag > 0) {
this.currentVal =
this.currentVal < this.maxNum
? this.currentVal + offset
: this.maxNum;
this.updateVal(this.currentVal);
this.updateCursor();
} else if (flag < 0) {
this.currentVal =
this.currentVal > this.minNum
? this.currentVal - offset
: this.minNum;
this.updateVal(this.currentVal);
this.updateCursor();
}
},
updateCursor() {
this.addBtnClass =
this.currentVal < this.maxNum
? "btn"
: "btn btn_is-disabled";
this.reduceBtnClass =
this.currentVal > this.minNum
? "btn"
: "btn btn_is-disabled";
},
buttonClick(flag) {
this.updateOffset(flag, this.step);
},
buttonMousedown(flag) {
this.isClickButton = true;
this.clearTimeInterval();
if (flag > 0) {
this.timeInterval = setInterval(() => {
this.buttonClick(1);
}, 100);
} else if (flag < 0) {
this.timeInterval = setInterval(() => {
this.buttonClick(-1);
}, 100);
}
},
buttonMouseOut() {
this.clearTimeInterval();
},
buttonMouseup() {
this.isClickButton = false;
this.clearTimeInterval();
},
clearTimeInterval() {
if (this.timeInterval == null) return;
clearInterval(this.timeInterval);
this.timeInterval = null;
},
},
};
</script>
<style scoped>
/**去掉上下箭头 */
.inputNumber-wrapper {
width: 180px;
height: 40px;
display: flex;
position: relative;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
/**输入框 */
.numboxInput {
text-align: center;
background-color: #fff;
background-image: none;
border-radius: 4px;
border: 1px solid #dcdfe6;
box-sizing: border-box;
color: #606266;
display: inline-block;
font-size: inherit;
height: 40px;
line-height: 40px;
outline: none;
padding: 0 15px;
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
width: 100%;
padding-left: 15px;
padding-right: 50px;
}
.numboxInput:hover {
border-color: #c0c4cc;
}
.numboxInput_changing {
border-color: #409eff;
}
.numboxInput:focus {
border-color: #409eff;
}
/**按钮们 */
.btn {
position: absolute;
z-index: 1;
width: 41px;
height: auto;
text-align: center;
background: #f5f7fa;
color: #606266;
cursor: pointer;
font-size: 13px;
line-height: 19px;
border: none;
border-right: none;
border-left: 1px solid #dcdfe6;
}
.btn:focus {
outline: none;
}
.btn:hover {
color: #409eff;
}
.btn_is-disabled {
cursor: not-allowed;
}
#reduceBtn {
right: 1px;
bottom: 1px;
top: auto;
left: auto;
border-radius: 0 0 4px 0;
}
#addBtn {
top: 1px;
right: 1px;
border-radius: 0 4px 0 0;
border-bottom: 1px solid #dcdfe6;
}
</style>