写之前要考虑:

  1. 键盘展开后,不能超过手机边缘
  2. 在底部展开键盘,键盘应出现在展开按钮上方;以此类推
  3. 重复点击展开按钮,关闭键盘

效果:
在这里插入图片描述

在这里插入图片描述
代码如下,有些按键逻辑还需要优化

<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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/86587.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/86587.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/86587.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《二分枚举答案(配合数据结构)》题集

文章目录 1、模板题集2、课内题集3、课后题集1. 字符串哈希2. 并查集3. ST表 1、模板题集 分巧克力 2、课内题集 倒水 冶炼金属 连续子序列的个数 3、课后题集 括号内的整数代表完整代码行数。 1. 字符串哈希 你猜猜是啥题(60) 2. 并查集 拯救萌萌(72) 3. ST表 GCD不小…

PY32F030单片机,优势替代ST GD,主频48MHz,带LED数码管驱动

PY32F030是一款高性能32位单片机&#xff0c;采用ARM Cortex-M0内核&#xff0c;工作频率高达48MHz&#xff0c;具备64KB Flash和8KB SRAM。它支持1.7V~5.5V宽电压范围&#xff0c;集成多路I2C、SPI、USART通讯外设&#xff0c;配备12位ADC、16位定时器和比较器&#xff0c;适用…

Rockchip Uboot中修改固件探测的存储介质

Rockchip Uboot中修改固件探测的存储介质 Rockchip uboot中支持从 eMMC、SDcard、NAND 、SPI_NAND、SPI_NOR等存储介质引导固件。 uboot的spl启动的时候会默认呢都会去探测这些介质&#xff0c;这样会导致探测时间变长&#xff0c;在实际产品中可以根据产品需求进行个性化的配…

动手学Python:从零开始构建一个“文字冒险游戏”

动手学Python&#xff1a;从零开始构建一个“文字冒险游戏” 大家好&#xff0c;我是你的技术向导。今天&#xff0c;我们不聊高深的框架&#xff0c;也不谈复杂的算法&#xff0c;我们来做一点“复古”又极具趣味性的事情——用Python亲手打造一个属于自己的文字冒险游戏&…

基于Kafka实现企业级大数据迁移的完整指南

在大数据时代&#xff0c;数据迁移已成为企业数字化转型过程中的常见需求。本文将详细介绍如何利用Kafka构建高可靠、高性能的大数据迁移管道&#xff0c;涵盖从设计到实施的完整流程。 一、为什么选择Kafka进行数据迁移&#xff1f; Kafka作为分布式消息系统&#xff0c;具有…

GEO引领品牌大模型种草:迈向Web3.0与元宇宙的认知新空间

在数字技术的演进历程中&#xff0c;我们正经历着从Web2.0到Web3.0、从平面互联网到沉浸式元宇宙的范式转变。这一转变不仅重塑了数字空间的形态和交互方式&#xff0c;更深刻改变了品牌与用户的连接模式和价值创造逻辑。而在这个新兴的数字疆域中&#xff0c;生成式引擎优化&a…

【机器学习与数据挖掘实战 | 医疗】案例18:基于Apriori算法的中医证型关联规则分析

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈机器学习与数据挖掘实战 ⌋ ⌋ ⌋ 机器学习是人工智能的一个分支,专注于让计算机系统通过数据学习和改进。它利用统计和计算方法,使模型能够从数据中自动提取特征并做出预测或决策。数据挖掘则是从大型数据集中发现模式、关联…

83、高级特性-自定义starter细节

83、高级特性-自定义starter细节 自定义Spring Boot Starter可以将通用功能封装成可复用的模块&#xff0c;简化其他项目的配置和使用。以下是创建自定义Starter的详细步骤和关键细节&#xff1a; ### 1. 项目结构 通常&#xff0c;自定义Starter包含两个模块&#xff1a; ####…

专注推理查询(ARQs):一种提升大型语言模型指令遵循度、决策准确性和防止幻觉的结构化方法

大型语言模型&#xff08;LLMs&#xff09;在客户服务、自动化内容创作和数据检索方面变得至关重要。然而&#xff0c;它们的有效性常常因其在多次交互中无法始终如一地遵循详细指令而受到限制。在金融服务和客户支持系统等高风险环境中&#xff0c;严格遵循指南是必不可少的&a…

华为云Flexus+DeepSeek征文 | DeepSeek驱动的医疗AI Agent:智能问诊系统开发完整指南

华为云FlexusDeepSeek征文 | DeepSeek驱动的医疗AI Agent&#xff1a;智能问诊系统开发完整指南 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇…

【大模型水印论文阅读2】前缀文本编码、均匀性约束

TOC &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f680; 感谢你的陪伴与支持~ 欢迎添加文末好友 &#x1f30c; 在所有感兴趣的领域扩展知识&#xff0c;不定期掉落福利资讯(*^▽^*) 写在最前面 版权声明&#xff1a;本文为原创&#xff0c;遵循 CC 4.0 BY-SA 协议。…

破茧时刻,与光同行

凌晨五点的闹钟刺破薄雾&#xff0c;我摸黑打开台灯。摊开的数学错题本上&#xff0c;函数图像在暖黄的光晕里舒展&#xff0c;像等待破译的密码。这样的清晨已持续三百多个日夜&#xff0c;我知道&#xff0c;在无数个相似的时刻里&#xff0c;总有千万盏台灯在黑暗中次第亮起…

Learning PostgresSQL读书笔记: 第8章 Triggers and Rules

本章将讨论以下内容&#xff1a; • 探索 PostgreSQL 中的规则 • 管理 PostgreSQL 中的触发器 • 事件触发器 探索 PostgreSQL 中的规则 文档中的这段话阐述了rule和trigger的区别&#xff1a; PostgreSQL 规则系统允许定义在数据库表中插入、更新或删除时执行的替代操作。粗…

信创国产化替代中的开发语言选择分析

在信息技术应用创新(信创)国产化替代过程中&#xff0c;选择合适的开发语言至关重要。以下是适合信创环境的开发语言及其优势分析&#xff1a; 主流适合信创的编程语言 1. Java 优势&#xff1a;跨平台特性(JVM)、丰富的生态体系、企业级应用成熟 信创适配&#xff1a;国内有…

Android 中 函数实现多个返回值的几种方式

在编程中&#xff0c;函数通常只能返回一个值。但通过使用对象封装、Pair、Triple、数组、列表或 Bundle 方式&#xff0c;可以轻松地返回多个值。 1、对象封装方式 创建数据类来封装需要返回的多个值。 data class Result(val code: Int, val message: String)fun getMultiV…

Leetcode百题斩-DP

又到了最好玩的dp了&#xff0c;各种玄学转移也算是其乐无穷。前段时间刚做的LCA正是这种题的小试牛刀&#xff0c;如果当时就把这个专题刷完了&#xff0c;或许我现在已经从西溪园区跑到云谷园区了。 不过&#xff0c;恐怖如斯的dp专题居然只给了一道hard&#xff0c;基本也没…

策略模式与工厂模式的黄金组合:从设计到实战

策略模式和工厂模式是软件开发中最常用的两种设计模式&#xff0c;当它们结合使用时&#xff0c;能产生11>2的效果。本文将通过实际案例&#xff0c;阐述这两种模式的协同应用&#xff0c;让代码架构更优雅、可维护性更强。 一、为什么需要组合使用&#xff1f; 单独使用的…

SAP PP模块与MM模块作用详解

SAP PP模块与MM模块作用详解 一、PP模块&#xff08;Production Planning&#xff09;—— 生产计划与执行中枢 核心作用&#xff1a;将销售需求转化为可执行的生产指令&#xff0c;管控从计划到完工的全过程。 关键功能 功能说明业务价值主数据管理维护BOM&#xff08;物料…

Linux tcp_info:监控TCP连接的秘密武器

深入解析 Linux tcp_info&#xff1a;TCP 状态的实时监控利器 在开发和运维网络服务时&#xff0c;我们常常遇到这些问题&#xff1a; 我的 TCP 连接为什么速度慢&#xff1f;是发生了重传&#xff0c;还是窗口太小&#xff1f;拥塞控制到底有没有生效&#xff1f; 这些问题…

CVE-2015-5531源码分析与漏洞复现(Elasticsearch目录遍历漏洞)

概述 漏洞名称&#xff1a;Elasticsearch 快照API目录遍历漏洞 CVE 编号&#xff1a;CVE-2015-5531 CVSS 评分&#xff1a;7.5 影响版本&#xff1a; Elasticsearch 1.0.0–1.6.0&#xff08;1.5.1及以前版本无需配置即可触发&#xff1b;1.5.2–1.6.0需配置path.repo&#xf…