react实现虚拟键盘支持Ant design Input和普通input Dom元素-升级篇

本文基于上一篇文章进行优化,不需要关注业务input怎么封装,不涉及业务改动。通用自定义数组键盘输入功能如下:直接贴代码

javascript 复制代码
import type { FC } from 'react';
import React, { useEffect } from 'react';
import { useRecoilValue } from 'recoil';

import { EPanelKeyModeType, EPanelNumCommand } from '@/pages/editor1/common/enum';
import ConfigManage from '@/pages/global/ConfigManage';
import { panelCommandStore } from '@/stores';
import { updateKeyMode } from '@/utils/panel';
export type AnyObjectImple = Record<string, string | number>;
const INPUTTAGNAME = 'INPUT';
const INPUTTEXT = 'text';
const INPUTTEL = 'tel';

/**
 * 通用业务数字键盘计算
 * @type cueMode: 文本模式,  cueMode: 数字模式两种
 * @waring 计算文本之前需要将文本现有数据 set 到panelNumberStore的originalVal上
 * @desc 考虑到这里业务的逻辑较复杂,将业务提取为公用业务组件
 * @desc 任何地方都可以使用该组件进行数字键盘的计算、文本的计算
 * @desc 追加文本框光标位置的计算
 * @returns
 */
const NumberKeyboard: FC = () => {
  // 指令
  const panelCommand = useRecoilValue(panelCommandStore);

  const IsNormalScreen = ConfigManage.getExpandCfg().IsNormalScreen;

  useEffect(() => {
    // 文字输入框添加onfocus, onblur监听,调起小控台数字按键
    if (IsNormalScreen) return;
    const isNeedBlur = (relateDM) => {
      if (relateDM?.tagName !== INPUTTAGNAME) {
        return true;
      } else {
        return relateDM?.type !== INPUTTEXT && relateDM?.type !== INPUTTEL;
      }
    };
    const isTargetInput = (targetDMParam: HTMLInputElement) => {
      return (
        targetDMParam?.tagName === INPUTTAGNAME &&
        (targetDMParam?.type === INPUTTEXT || targetDMParam?.type === INPUTTEL)
      );
    };
    const onFocus = (e: FocusEvent) => {
      if (isTargetInput(e.target as HTMLInputElement)) {
        updateKeyMode(EPanelKeyModeType.numberKeyMode);
      }
    };
    const onBlur = (e: FocusEvent) => {
      const relateDOM = e.relatedTarget as HTMLInputElement;
      if (isTargetInput(e.target as HTMLInputElement) && isNeedBlur(relateDOM)) {
        updateKeyMode(EPanelKeyModeType.restoreLastMode);
      }
    };
    document.addEventListener('focusin', onFocus);
    document.addEventListener('focusout', onBlur);
    return () => {
      document.removeEventListener('focusin', onFocus);
      document.removeEventListener('focusout', onBlur);
    };
  }, []);
  /**
   * 数字按键相关指令
   * @desc 1. 处理普通输入框
   * @desc 2. 处理纯数字按键模式
   * @return input 结果
   */
  const changeNumberCommand = (commandData) => {
    // 指令
    const { command } = commandData;
    try {
      // 获取选定对象
      const selection = window.getSelection();
      if (selection) {
        // 如果是文本节点则先获取光标对象
        const range = selection?.getRangeAt(0);
        // 获取光标对象的范围界定对象,一般就是textNode对象
        const textNode: any = range.startContainer;
        // 获取输入框input Dom元素, 使用的是And design Input,textNode是div,div里面且套input等子元素
        const inputNode = textNode?.getElementsByTagName('input')[0];
        // 光标开始
        const selectionStart = inputNode.selectionStart;
        // 结束位置
        const selectionEnd = inputNode.selectionEnd;
        console.info('selectionStart', selectionStart, 'selectionEnd', selectionEnd);
        // 处理指令运算: 数字
        handleNumberCommand(command, inputNode);
      }
    } catch (e) {}
  };
  /**
   * 数字指令处理
   * @param command
   * @param inputNode
   */
  const handleNumberCommand = (command, inputNode) => {
    // 业务指令转对应文本
    let pressFpgaVal: number | string = command - EPanelNumCommand.num0;
    if (command === EPanelNumCommand.point) {
      pressFpgaVal = '.';
    }
    /**
     * 1. 处理键盘指令,分数字模式和非数字模式,非数字模式直接追加内容,数字模式计算内容
     * 2. 判断指令范围
     * 3. 判断指令模式,处理对应模式数据
     */
    if (command >= EPanelNumCommand.num0 && command <= EPanelNumCommand.enter) {
      if (command <= EPanelNumCommand.point) {
        // 0-9数字处理指令
        doNumberCommand(inputNode, pressFpgaVal);
      } else if (command === EPanelNumCommand.point) {
        // 小数点操作
        // equalTen();
      } else if ([EPanelNumCommand.add, EPanelNumCommand.minus].includes(command)) {
        // 运算指令暂时不支持
        // opertBtn(command);
      } else if (command === EPanelNumCommand.back) {
        // 控台Back删除处理指令
        doBackCommand(inputNode);
      } else if (command === EPanelNumCommand.enter) {
        // 控台Enter处理指令
        doEnterCommand(inputNode);
      }
    }
  };
  /**
   * 0-9数字处理指令
   * @param command
   */
  const doNumberCommand = (inputNode, pressFpgaVal) => {
    // 转换成字符串
    const value = inputNode.value + '';
    // 光标开始、结束位置
    const selectionStart = inputNode.selectionStart;
    const selectionEnd = inputNode.selectionEnd;
    // 计算新值
    const newValue =
      value.substr(0, selectionStart) + pressFpgaVal + value.substring(selectionEnd, value.length);
    console.info('value', value, newValue);

    // 事件发布到组件中input
    const evt = new UIEvent('inputchange', {
      bubbles: false,
      cancelable: false,
    });
    const keyName = 'data';
    evt[keyName] = {
      value: newValue,
      selectionStart: selectionStart + pressFpgaVal.toString().length,
      selectionEnd: selectionStart + pressFpgaVal.toString().length,
    };
    // 触发onChange事件
    inputNode.dispatchEvent(evt, newValue);
  };
  /**
   * 控台Back删除处理指令
   * @param command
   */
  const doBackCommand = (inputNode) => {
    // 转换成字符串
    const value = inputNode.value + '';
    // 光标开始、结束位置
    const selectionStart = inputNode.selectionStart;
    const selectionEnd = inputNode.selectionEnd;
    // 计算新值
    const newValue =
      value.substr(0, selectionStart - 1) + value.substring(selectionEnd, value.length);
    console.info('value', value, newValue);

    // 事件发布到组件中input
    const evt = new UIEvent('inputchange', {
      bubbles: false,
      cancelable: false,
    });
    const keyName = 'data';
    evt[keyName] = {
      value: newValue,
      selectionStart: selectionStart - 1,
      selectionEnd: selectionStart - 1,
    };
    // 触发onChange事件
    inputNode.dispatchEvent(evt);
  };
  /**
   * 控台Enter处理指令
   * 触发失去焦点指令
   * @param command
   */
  const doEnterCommand = (inputNode) => {
    // 失焦事件发布到组件中input
    const evt = new UIEvent('enter', {
      bubbles: false,
      cancelable: false,
    });
    // 触发onChange事件
    inputNode.dispatchEvent(evt);
  };
  /**
   * 初始化订阅数字键盘指令
   */
  useEffect(() => {
    if (panelCommand) {
      changeNumberCommand(panelCommand);
    }
  }, [panelCommand]);

  return <div />;
};

export default NumberKeyboard;
相关推荐
程序员清洒21 分钟前
Flutter for OpenHarmony:Icon 与 IconButton — 图标系统集成
前端·学习·flutter·华为
萧曵 丶27 分钟前
JavaScript 函数各种写法和场景
开发语言·javascript·ecmascript
Yolanda941 小时前
【项目经验】钉钉免密登录实现
前端·javascript·钉钉
2601_949613021 小时前
flutter_for_openharmony家庭药箱管理app实战+药品详情实现
java·前端·flutter
摘星编程1 小时前
在OpenHarmony上用React Native:collapsable节点优化策略
javascript·react native·react.js
We་ct2 小时前
LeetCode 15. 三数之和:排序+双指针解法全解析
前端·算法·leetcode·typescript
美狐美颜SDK开放平台2 小时前
直播场景下抖动特效的实现方案:美颜sdk开发经验分享
前端·人工智能·美颜sdk·直播美颜sdk·视频美颜sdk
Beginner x_u2 小时前
JavaScript 原型、原型链与原型继承的核心机制解析
开发语言·javascript·原型模式·原型原型链
草青工作室2 小时前
java-FreeMarker3.4自定义异常处理
java·前端·python
美狐美颜sdk2 小时前
抖动特效在直播美颜sdk中的实现方式与优化思路
前端·图像处理·人工智能·深度学习·美颜sdk·直播美颜sdk·美颜api