屏幕键盘组件

需求场景

平板的弹窗需要自带一个键盘弹窗输入

实现

基于 Vue 3 和 Simple Keyboard 库

<template>
  <div
    ref="draggableDiv"
    class="draggable"
    @mousedown="onMouseDown"
    @touchstart="onTouchStart"
  >
    <div v-show="visible" class="keyboard-content">
      <div :class="keyboardClass"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, watch, onMounted, onUnmounted } from 'vue'
  import Keyboard from 'simple-keyboard'
  import 'simple-keyboard/build/css/index.css'
  import useVisible from '@/hooks/visible'

  const props = defineProps({
    modalValue: {
      type: String,
    },
    keyboardClass: {
      type: String,
      default: 'simple-keyboard',
    },
    input: {
      type: String,
    },
    maxLength: {
      type: Number,
      default: 100,
    },
  })

  const keyboard = ref()
  const inputValue = ref()

  const emit = defineEmits(['change', 'enter'])

  /**   hooks   */
  const { visible, setVisible } = useVisible(false)

  const onChange = (value: string) => {
    emit('change', value)
  }

  const displayDefault = ref({
    '{bksp}': 'backspace',
    '{lock}': 'caps',
    '{enter}': 'enter',
    '{tab}': 'tab',
    '{shift}': 'shift',
    '{change}': 'en',
    '{space}': 'space',
    '{clear}': 'del',
    '{close}': '退出',
    '{arrowleft}': '←',
    '{arrowright}': '→',
  })

  const open = () => {
    visible.value = !visible.value
  }

  const handleShift = () => {
    const currentLayout = keyboard.value.options.layoutName
    const shiftToggle = currentLayout === 'default' ? 'shift' : 'default'
    keyboard.value.setOptions({
      layoutName: shiftToggle,
    })
  }

  const clean = () => {
    keyboard.value.clearInput()
    setVisible(false)
  }
  const onKeyPress = (button: any) => {
    /**
     * 如果你想操作shift键和大写锁定键
     */
    if (button === '{shift}' || button === '{lock}') handleShift()
    if (button === '{enter}') {
      emit('enter')
    }
    if (button === '{clear}') {
      keyboard.value.clearInput()
    }
    if (button === '{close}') {
      setVisible(false)
    }
  }

  watch(
    () => props.modalValue,
    (newvalue) => {
      if (newvalue !== inputValue.value) {
        keyboard.value.setInput(newvalue)
      }
    }
  )

  /**
   * 实现键盘的拖拽
   */

  // 记录鼠标或触摸初始位置和元素初始位置
  let startX = 0
  let startY = 0
  let elementX = 0
  let elementY = 0

  const draggableDiv = ref()

  const onTouchMove = (event: any) => {
    const touch = event.touches[0]
    const deltaX = touch.clientX - startX
    const deltaY = touch.clientY - startY

    draggableDiv.value.style.left = `${elementX + deltaX}px`
    draggableDiv.value.style.top = `${elementY + deltaY}px`
  }

  const onTouchEnd = () => {
    document.removeEventListener('touchmove', onTouchMove)
    document.removeEventListener('touchend', onTouchEnd)
  }

  const onMouseMove = (event: any) => {
    const deltaX = event.clientX - startX
    const deltaY = event.clientY - startY

    draggableDiv.value.style.left = `${elementX + deltaX}px`
    draggableDiv.value.style.top = `${elementY + deltaY}px`
  }

  const onMouseUp = () => {
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
  }

  const onMouseDown = (event: any) => {
    startX = event.clientX
    startY = event.clientY
    elementX = draggableDiv.value.offsetLeft
    elementY = draggableDiv.value.offsetTop

    document.addEventListener('mousemove', onMouseMove)
    document.addEventListener('mouseup', onMouseUp)
  }

  const onTouchStart = (event: any) => {
    const touch = event.touches[0]
    startX = touch.clientX
    startY = touch.clientY
    elementX = draggableDiv.value.offsetLeft
    elementY = draggableDiv.value.offsetTop

    document.addEventListener('touchmove', onTouchMove)
    document.addEventListener('touchend', onTouchEnd)
  }

  defineExpose({
    open,
    clean,
  })

  onMounted(() => {
    keyboard.value = new Keyboard(props.keyboardClass, {
      onChange,
      onKeyPress,
      useMouseEvents: true,
      maxLength: {
        default: props.maxLength,
      },
      preventMouseDownDefault: false,
      layout: {
        // 默认布局
        default: [
          '` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
          '{close} q w e r t y u i o p [ ] \\',
          "{lock} a s d f g h j k l ; ' {enter}",
          '{change} z x c v b n m , . / {clear}',
          '{space}',
        ],
        // 大小写
        shift: [
          '~ ! @ # $ % ^ & * ( ) _ + {bksp}',
          '{close} Q W E R T Y U I O P { } |',
          '{lock} A S D F G H J K L : " {enter}',
          '{change} Z X C V B N M < > ? {clear}',
          '{space}',
        ],
        // 数字布局
        number: [
          '7 8 9',
          '4 5 6',
          '1 2 3',
          '. 0 {bksp}',
          '{arrowleft} {arrowright} {clear} {close}',
        ],
      },
      layoutName: 'default',
      display: displayDefault.value,
    })
  })

  onUnmounted(() => {
    document.removeEventListener('mousemove', onMouseMove)
    document.removeEventListener('mouseup', onMouseUp)
    document.removeEventListener('touchmove', onTouchMove)
    document.removeEventListener('touchend', onTouchEnd)
  })
</script>

<style scoped lang="less">
  .draggable {
    position: absolute;
    cursor: move;
    bottom: calc(12vh);
    z-index: 1001;

    .keyboard-content {
      width: 1200px;
      font-size: 32px;
      font-weight: 500;

      :deep(.hg-theme-default .hg-button) {
        height: 55px;
      }
      :deep(.hg-theme-default .hg-button.hg-standardBtn:active) {
        background-color: rgb(var(--gray-3));
      }
    }
  }
</style>

介绍

主要功能

  1. 虚拟键盘输入:提供全功能的虚拟键盘输入,支持字母、数字、符号等

  2. 拖拽功能:整个键盘区域可以通过鼠标或触摸屏拖拽移动位置

  3. 布局切换:支持默认布局、大写布局和数字布局三种键盘布局

  4. 自定义按键:提供清除、关闭、回车等特殊功能按键

核心实现

键盘功能
  • 使用 simple-keyboard 库实现核心键盘功能

  • 通过 onChangeonKeyPress 事件处理输入和特殊按键

  • 支持通过 handleShift 方法切换大小写布局

  • 提供 displayDefault 对象自定义按键显示文本

拖拽功能
  • 使用 mousedown/touchstart 事件开始拖拽

  • 通过 mousemove/touchmove 计算移动距离并更新位置

  • mouseup/touchend 时移除事件监听

  • 使用绝对定位 (position: absolute) 实现自由移动

组件通信
  • 通过 props 接收初始值 (modalValue)、键盘类名 (keyboardClass) 等

  • 通过 emit 发送 changeenter 事件

  • 使用 defineExpose 暴露 openclean 方法供外部调用

使用方式

<simple-keyboard
        ref="keyboardRef"
        :max-length="20"
        :modal-value="state.lockScreenPassword"
        @change="getInput"
        @enter="unlockScreen"
      />

keyboardRef.value.open()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值