写之前要考虑:
- 键盘展开后,不能超过手机边缘
- 在底部展开键盘,键盘应出现在展开按钮上方;以此类推
- 重复点击展开按钮,关闭键盘
效果:
代码如下,有些按键逻辑还需要优化
<template>
<view class="container">
<!-- 可拖动按钮 -->
<view
class="drag-btn"
:style="{left: btnLeft + 'px', top: btnTop + 'px'}"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
@click="showKeyboard"
>
<text>键盘</text>
</view>
<!-- 小键盘 -->
<view
v-if="isShowKeyBoard"
class="keyboard"
:style="keyboardStyle"
>
<view v-for="(row, rowIndex) in keyboardLayout" :key="rowIndex" class="keyboard-row">
<view
v-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.btnLeft
let 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.windowWidth
this.screenHeight = res.windowHeight
// 初始位置设置为屏幕中间
this.btnLeft = (res.windowWidth - 50) / 2
this.btnTop = (res.windowHeight - 50) / 2
}
})
},
methods: {
onTouchStart (e) {
this.startX = e.touches[0].clientX
this.startY = e.touches[0].clientY
this.isDragging = false
// 隐藏键盘
// this.isShowKeyBoard = false
},
onTouchMove (e) {
const moveX = e.touches[0].clientX - this.startX
const moveY = e.touches[0].clientY - this.startY
// 移动距离大于5px才认为是拖动
if (Math.abs(moveX) > 5 || Math.abs(moveY) > 5) {
this.isDragging = true
}
// 计算新位置
let newLeft = this.btnLeft + moveX
let 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 = newLeft
this.btnTop = newTop
// 更新起始位置,实现连续拖动
this.startX = e.touches[0].clientX
this.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>