写之前要考虑:
- 键盘展开后,不能超过手机边缘
- 在底部展开键盘,键盘应出现在展开按钮上方;以此类推
- 重复点击展开按钮,关闭键盘
效果:
代码如下,有些按键逻辑还需要优化
<template><view class="container"><!-- 可拖动按钮 --><viewclass="drag-btn":style="{left: btnLeft + 'px', top: btnTop + 'px'}"@touchstart="onTouchStart"@touchmove="onTouchMove"@touchend="onTouchEnd"@click="showKeyboard"><text>键盘</text></view><!-- 小键盘 --><viewv-if="isShowKeyBoard"class="keyboard":style="keyboardStyle"><view v-for="(row, rowIndex) in keyboardLayout" :key="rowIndex" class="keyboard-row"><viewv-for="(key, keyIndex) in row":key="keyIndex"class="key":class="key.className || ''"@click="onKeyPress($event,key.value)">{{ key.name }}</view></view></view></view>
</template><script>
export default {data () {return {btnLeft: 100, // 按钮初始位置btnTop: 100,startX: 0, // 触摸起始位置startY: 0,isDragging: false, // 是否正在拖动isShowKeyBoard: false, // 是否显示键盘screenWidth: 375, // 屏幕宽度,会在onLoad中获取实际值screenHeight: 667, // 屏幕高度keyboardWidth: 330, // 键盘宽度keyboardHeight: 170, // 键盘高度// 键盘布局keyboardLayout: [// 第一行[{name: 'Esc', value: 0x1},{name: '1', value: 0x2},{name: '2', value: 0x3},{name: '3', value: 0x4},{name: '4', value: 0x5},{name: '5', value: 0x6},{name: '6', value: 0x7},{name: '7', value: 0x8},{name: '8', value: 0x9},{name: '9', value: 0xa},{name: '0', value: 0xb},{name: '-', value: 0xc},{name: '=', value: 0xd},{name: '←', value: 0xe, className: 'wide-key'}],// 第二行[{name: 'Tab', value: 0xf, className: 'wide-key'},{name: 'q', value: 0x10},{name: 'w', value: 0x11},{name: 'e', value: 0x12},{name: 'r', value: 0x13},{name: 't', value: 0x14},{name: 'y', value: 0x15},{name: 'u', value: 0x16},{name: 'i', value: 0x17},{name: 'o', value: 0x18},{name: 'p', value: 0x19},{name: '[', value: 0x1a},{name: ']', value: 0x1b},{name: '\\', value: 0x2b}],// 第三行[{name: 'Caps', value: 0x3a, className: 'wide-key'},{name: 'a', value: 0x1e},{name: 's', value: 0x1f},{name: 'd', value: 0x20},{name: 'f', value: 0x21},{name: 'g', value: 0x22},{name: 'h', value: 0x23},{name: 'j', value: 0x24},{name: 'k', value: 0x25},{name: 'l', value: 0x26},{name: ';', value: 0x27},{name: '\'', value: 0x28},{name: 'Enter', value: 0x1c, className: 'wide-key'}],// 第四行[{name: 'Shift', value: 0x2a, className: 'extra-wide-key'},{name: 'z', value: 0x2c},{name: 'x', value: 0x2d},{name: 'c', value: 0x2e},{name: 'v', value: 0x2f},{name: 'b', value: 0x30},{name: 'n', value: 0x31},{name: 'm', value: 0x32},{name: ',', value: 0x33},{name: '.', value: 0x34},{name: '/', value: 0x35},{name: 'Shift', value: 0x36, className: 'extra-wide-key'}],// 第五行[{name: 'Ctrl', value: 0x1d, className: 'wide-key'},{name: 'Alt', value: 0x38, className: 'wide-key'},{name: '空格', value: 0x39, className: 'space-key'},{name: 'Alt', value: 0x138, className: 'wide-key'},{name: 'Ctrl', value: 0x11d, className: 'wide-key'}]]}},computed: {// 计算键盘位置样式keyboardStyle () {let left = this.btnLeftlet top = this.btnTop + 50 // 默认在按钮下方// 如果键盘会超出右边界,则放在左侧if (left + this.keyboardWidth > this.screenWidth) {left = this.btnLeft - this.keyboardWidth}// 如果键盘会超出下边界,则放在上方if (top + this.keyboardHeight > this.screenHeight) {top = this.btnTop - this.keyboardHeight}// 确保不超出左边界和上边界left = Math.max(0, left)top = Math.max(0, top)return {left: left + 'px',top: top + 'px',width: this.keyboardWidth + 'px',height: this.keyboardHeight + 'px'}}},onLoad () {// 获取屏幕尺寸uni.getSystemInfo({success: (res) => {this.screenWidth = res.windowWidththis.screenHeight = res.windowHeight// 初始位置设置为屏幕中间this.btnLeft = (res.windowWidth - 50) / 2this.btnTop = (res.windowHeight - 50) / 2}})},methods: {onTouchStart (e) {this.startX = e.touches[0].clientXthis.startY = e.touches[0].clientYthis.isDragging = false// 隐藏键盘// this.isShowKeyBoard = false},onTouchMove (e) {const moveX = e.touches[0].clientX - this.startXconst moveY = e.touches[0].clientY - this.startY// 移动距离大于5px才认为是拖动if (Math.abs(moveX) > 5 || Math.abs(moveY) > 5) {this.isDragging = true}// 计算新位置let newLeft = this.btnLeft + moveXlet newTop = this.btnTop + moveY// 限制不超出屏幕边界newLeft = Math.max(0, Math.min(newLeft, this.screenWidth - 50))newTop = Math.max(0, Math.min(newTop, this.screenHeight - 50))// 更新位置this.btnLeft = newLeftthis.btnTop = newTop// 更新起始位置,实现连续拖动this.startX = e.touches[0].clientXthis.startY = e.touches[0].clientY// 阻止默认行为和冒泡e.preventDefault()e.stopPropagation()},onTouchEnd (e) {if (this.isDragging) {// 如果是拖动操作,阻止点击事件e.preventDefault()e.stopPropagation()}this.isDragging = false},showKeyboard () {if (!this.isDragging) {this.isShowKeyBoard = !this.isShowKeyBoard}},onKeyPress (e, key) {this.$emit('onKeyPress', e, key)// spiceInterface.onKeyUpdate(Number(key), true)// spiceInterface.onKeyUpdate(Number(key), false)// 这里可以添加按键处理逻辑// 例如:uni.vibrateShort() 震动反馈}}
}
</script><style scoped>
.container {position: relative;/* width: 100%;height: 100vh; */
}.drag-btn {position: fixed;width: 45px;height: 45px;background-color: #007AFF;border-radius: 25px;display: flex;justify-content: center;align-items: center;color: white;font-size: 12px;z-index: 999;touch-action: none; /* 防止触摸事件的默认行为 */
}.keyboard {position: fixed;background-color: #f0f0f0;border-radius: 8px;padding: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);z-index: 998;
}.keyboard-row {display: flex;justify-content: center;margin-bottom: 5px;
}.key {width: 25px;height: 30px;margin: 0 2px;background-color: white;border-radius: 4px;display: flex;justify-content: center;align-items: center;font-size: 14px;box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.wide-key {width: 45px !important; /* 比普通键宽 */
}.extra-wide-key {width: 80px !important; /* 更宽的键 */
}.space-key {width: 200px !important; /* 空格键特别宽 */
}
</style>