vue 数字软键盘 插件 封装 可拖动

1、效果图 

4341ddb9a2c64ad194c71f6d4d8ea940.gif

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>

4、完结 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/141426.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《QT从基础到进阶·二十四》按钮组QButtonGroup,单选框QRadioButton和多选框QCheckBox

1、按钮组QButtonGroup 如果有多个单选按钮&#xff0c;可以统一放进一个按钮组。 图中有三个单选按钮放进了一个QGroupBox,并且设置了水平布局&#xff0c;现在要将这三个单选按钮放进一个按钮组&#xff0c;之前的想法是先把三个按钮加入按钮组&#xff0c;再把按钮组放进QG…

图的表示与基础--Java

1.图的基础知识 该图片来自于&#xff1a; https://b23.tv/KHCF2m6 2.稀疏图与稠密图 G(V,E)&#xff1a;V顶点个数&#xff0c;E边的个数 稀疏图&#xff1a;E<<V 一般用邻接表表示(数组链表) 稠密图&#xff1a;E接近V 一般用邻接矩阵表示&#xf…

S32K3基础学习 linker链接器脚本ld文件的学习(一)

一、简介 最近学习NXP新推出的S32K3系列芯片&#xff0c;我在学习容易转牛角尖&#xff0c;非得要搞明白这个芯片的启动流程&#xff0c;所以花费了一些时间&#xff0c;进行查阅资料进行学习&#xff0c;这里做下详细的记录&#xff0c;希望有用&#xff0c;如果有错误欢迎指正…

海上船舶交通事故VR模拟体验低成本高效率-深圳华锐视点

在海上运输行业&#xff0c;安全事故的防范和应对能力是企业安全教育的重中之重。针对这一问题&#xff0c;海上运输事故VR模拟逃生演练成为了一种创新且高效的教育手段。通过这种演练&#xff0c;企业能够在提升员工安全意识和技能方面获得多方面的帮助。 在VR船舶搜救演练中&…

第十五章,输入输出流例题

package 例题;import java.io.File;public class 例题1 {public static void main(String[] args) {//创建文件对象File file new File("D:\\Java15-1.docx");//判断&#xff0c;如果该文件存在。exists存在的意思if (file.exists()) {//删除//file.delete();//Syst…

轻量封装WebGPU渲染系统示例<28>- MRT纹理(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/MRT.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: export class MRT {private mRscene new RendererScene();initial…

拼多多商品详情API接口接入流程如下:

拼多多商品详情API接口可以用于获取拼多多商品的具体信息&#xff0c;包括商品ID、商品名称、价格、销量、评价等。以下是使用拼多多商品详情API接口的步骤&#xff1a; 进入拼多多开放平台&#xff0c;注册并登录账号。在开放平台页面中&#xff0c;找到“商品详情”或“商品…

DDD领域驱动设计模式结构图面向接口编程

DDD领域驱动设计模式结构图面向接口编程 9.资源库 在刚接触资源库(Repository)时&#xff0c;第一反应便是这就是个 DAO 层&#xff0c;访问数据库&#xff0c;然后吧啦吧啦&#xff0c;但是&#xff0c;当接触的越久&#xff0c;越发认识到第一反应是错的&#xff0c;资源库更…

No194.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

azkaban的安装

一、下载上传文件 二、创建目录 mkdir /opt/soft/azkaban 三、解压 tar -zxvf /opt/install/azkaban-db-3.84.4.tar.gz -C /opt/soft/azkaban tar -zxvf /opt/install/azkaban-exec-server-3.84.4.tar.gz -C /opt/soft/azkaban tar -zxvf /opt/install/azkaban-web-server-…

Ultipa 支持OpenCypher,助力企业级应用发展

OpenCypher 是欧美图数据库厂家 Neo4j 基于其图查询语言Cypher 开发的一套开源图查询语言&#xff0c;该语言也是开发者们较为熟悉的图查询语言之一。 Ulitpa Graph&#xff08;嬴图&#xff09;于2022年6月实现的对OpenCypher 的支持&#xff0c;旨在让用户能够通过自己熟悉的…

APP攻防-资产收集篇反证书检验XP框架反代理VPN数据转发反模拟器

文章目录 常见问题防护手段 常见问题 没有限制过滤的抓包问题&#xff1a; 1、抓不到-工具证书没配置好 2、抓不到-app走的不是http/s 有限制过滤的抓包问题&#xff1a; 3、抓不到-反模拟器调试 4、抓不到-反代理VPN 5、抓不到-反证书检验 做移动安全测试时&#xff0c;设置…

Java中所有的运算符,以及运算符优先级(总结)

运算法是一种特殊的符号&#xff0c;用于表示数据的运算、复制、比较等。 1、算数运算符 // % 取余运算&#xff1a;结果的符号和被模数的符号一致 12 % 5 2 -12 % 5 -2 12 % -5 2 -12 % -5 -2int a1 10; int b1 a1; // a111, b111 int a2 10; int b2 a2; // a211, …

频域分析实践介绍

频域分析实践介绍 此示例说明如何执行和解释基本频域信号分析。该示例讨论使用信号的频域表示相对于时域表示的优势&#xff0c;并使用仿真数据和真实数据说明基本概念。该示例回答一些基本问题&#xff0c;例如&#xff1a;FFT 的幅值和相位的含义是什么&#xff1f;我的信号是…

谈谈Vue双向数据绑定的原理

目录 一、什么是Vue.js 二、什么是双向数据绑定 三、双向数据绑定的原理 一、什么是Vue.js Vue.js是一款流行的JavaScript前端框架&#xff0c;用于构建用户界面。它是一个轻量级、灵活而高效的框架&#xff0c;被广泛应用于单页应用程序和可交互的前端界面开发。Vue.js的设…

Jmeter+ant+Jenkins持续集成

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

同济 MBA 携手和鲸课程共建,以数智人才培养持续赋能企业数字化转型

数智化的浪潮席卷全球&#xff0c;我国产业界应如何做出应变&#xff1f;各企业又该如何深化数字化转型&#xff1f;在任重道远的持续探索中&#xff0c;数智人才培养作为企业实现成功转型的关键要素&#xff0c;已然成为大势所趋。 同济大学综合 MBA 项目高度重视工商管理人才…

2023年【建筑电工(建筑特殊工种)】找解析及建筑电工(建筑特殊工种)复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 建筑电工(建筑特殊工种)找解析是安全生产模拟考试一点通生成的&#xff0c;建筑电工(建筑特殊工种)证模拟考试题库是根据建筑电工(建筑特殊工种)最新版教材汇编出建筑电工(建筑特殊工种)仿真模拟考试。2023年【建筑电…

CTFhub-RCE-文件包含

访问网站 我们仔细看一下php代码&#xff0c;重点是 if (!strpos($_GET["file"], "flag")) { include $_GET["file"]; 这里有一个strpos(string,find,start)函数 意思在string字符串中找find的位置,start是查找的开始位置 那么这句代…

设计原则 | 单一职能原则

一、单一职能原则&#xff08;SRP&#xff1a;Single Responsibility Principle&#xff09; 1、原理 就一个类而言&#xff0c;应该仅有一个引起它变化的原因。如果一个类承担的职责过多&#xff0c;就等于把这些职责耦合在一起。一个职责的变化可能会削弱或者抑制这个类完成…