1、效果图
2、使用方式
<Keyboard v-if="show" @close="show = false" :inputDom="$refs.input" />
封装的数字键盘 Keyboard.vue 组件代码
<template>
<div
class="keyboard"
ref="keyboard"
:style="{ left: moveX + 'px', bottom: moveY + 'px' }"
>
<div class="drag" @mousedown="keyDown">
<p></p>
<div @click="$emit('close')" @mousedown.stop @mousemove.stop>
<img src="./arrow.svg" alt="" />
</div>
</div>
<div class="main">
<div class="left">
<div
v-for="text in symbols"
:key="text"
@click="insertTxtAndSetcursor(text)"
>
{{ text }}
</div>
</div>
<div class="right">
<div class="left">
<div
class="item"
v-for="i in 9"
:key="i"
@click="insertTxtAndSetcursor(i)"
>
{{ i }}
</div>
</div>
<div class="right">
<div class="item" @click="insertTxtAndSetcursor('', true)">
<img src="./delete.svg" alt="" />
</div>
<div class="item" @click="insertTxtAndSetcursor(' ')">
<img src="./blank.svg" alt="" />
</div>
<div class="item" @click="insertTxtAndSetcursor(0)">0</div>
</div>
</div>
</div>
</div>
</template>
<script scoped>
export default {
data() {
return {
symbols: ["+", "-", "=", ".", "/", "@"],
flag: false,
seto: null,
downInfo: {},
moveX: 0,
moveY: 0,
};
},
props: {
inputDom: {
type: Node,
default: null,
},
},
mounted() {
document.addEventListener("mousemove", this.keyMove);
},
methods: {
// 输入文本
insertTxtAndSetcursor(text, del) {
let element = this.inputDom; // 获取到指定标签
let startPos = element.selectionStart; // 获取光标开始的位置
let endPos = element.selectionEnd; // 获取光标结束的位置
if (startPos === undefined || endPos === undefined) return; // 如果没有光标位置 不操作
let oldTxt = element.value; // 获取输入框的文本内容
let result = "";
// 光标位置不能小于0
const num = startPos - 1;
if (del && num >= 0) {
result = oldTxt.substring(0, startPos - 1) + oldTxt.substring(endPos); // 将文本插入
} else {
result =
oldTxt.substring(0, startPos) + text + oldTxt.substring(endPos); // 将文本插入
}
element.value = result; // 将拼接好的文本设置为输入框的值
element.focus(); // 重新聚焦输入框
if (del && num >= 0) {
element.selectionStart = startPos - 1 + (text + "").length; // 设置光标开始的位置
element.selectionEnd = startPos - 1 + (text + "").length; // 设置光标结束的位置
} else {
element.selectionStart = startPos + (text + "").length; // 设置光标开始的位置
element.selectionEnd = startPos + (text + "").length; // 设置光标结束的位置
}
},
keyUp() {
this.flag = false;
document.removeEventListener("mouseup", this.keyUp);
document.removeEventListener("mouseleave", this.keyUp);
},
keyDown(e) {
this.downInfo.x = e.pageX;
this.downInfo.y = e.pageY;
this.downInfo.left = this.moveX;
this.downInfo.bottom = this.moveY;
this.seto = setTimeout(() => {
this.flag = true;
document.addEventListener("mouseup", this.keyUp);
document.addEventListener("mouseleave", this.keyUp);
clearTimeout(this.seto);
}, 1000);
},
keyMove(e) {
if (this.flag) {
const maxh =
(document.clientHeight ||
document.documentElement.clientHeight ||
document.body.clientHeight) - this.$refs.keyboard.clientHeight;
const htj = this.downInfo.bottom + this.downInfo.y - e.pageY;
const maxw =
(document.clientWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth) - this.$refs.keyboard.clientWidth;
const wtj = this.downInfo.left - (this.downInfo.x - e.pageX);
if (wtj <= maxw && wtj >= 0) {
//确保键盘始终在屏幕可见范围内
this.moveX = wtj;
}
if (htj <= maxh && htj >= 0) {
//确保键盘始终在屏幕可见范围内
this.moveY = htj;
}
}
},
},
};
</script>
<style lang="scss" scoped>
.keyboard {
width: 100vw;
height: 245px;
max-width: 353px;
min-width: 285px;
position: fixed;
bottom: 0;
left: 0;
background-color: #d6d7db;
> .drag {
width: 100%;
height: 15%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
> p {
width: 30%;
height: 13%;
border-radius: 5px;
background-color: #fff;
}
> div {
width: 18.3%;
height: 100%;
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
&::before {
width: 1px;
content: "";
height: 60%;
background-color: #fff;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
}
> img {
user-select: none;
width: 45%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
> .main {
height: calc(100% - 15%);
display: flex;
justify-content: space-between;
padding: 2%;
> .left {
flex: 15;
margin-right: 2%;
height: 100%;
border-radius: 6px;
background-color: #fff;
overflow: auto;
user-select: none;
&::-webkit-scrollbar {
display: none;
}
> div {
font-size: 20px;
height: 25%;
display: flex;
align-items: center;
justify-content: center;
&:active {
background-color: #a1a8b8;
}
}
}
> .right {
height: 100%;
border-radius: 5px;
flex: 77;
display: flex;
justify-content: space-between;
user-select: none;
> .left {
height: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-content: space-between;
width: 77%;
> .item {
background-color: #fff;
height: 30.8%;
width: 31.5%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
font-size: 18px;
&:active {
background-color: #adb4be;
}
&:nth-of-type(3n) {
margin-right: 0;
}
&:nth-of-type(7),
&:nth-of-type(8),
&:nth-of-type(9) {
margin-bottom: 0;
}
}
}
> .right {
width: 21%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
> .item {
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
height: 30.8%;
background-color: #adb4be;
> img {
width: 40%;
}
&:active {
background-color: #7e8492;
}
}
}
}
}
}
</style>
3、使用方式代码
<template>
<div id="app">
<input ref="input" type="text" @focus="show = true" v-model="text" />
<Keyboard v-if="show" @close="show = false" :inputDom="$refs.input" />
</div>
</template>
<script scoped>
import Keyboard from "./Keyboard.vue";
export default {
components: { Keyboard },
data() {
return { show: false, text: "" };
},
};
</script>
<style lang="scss" scoped>
#app {
width: 100vw;
height: 100vh;
background-color: #000;
> input {
height: 25px;
display: block;
margin: 0 auto;
}
}
</style>