使用文本框模拟密码框

前景提要

还是先说问题,之前做过的一个移动端项目中,要做一个登陆功能,很基础很常见,但是密码框这块遇到一个问题,密码框在一些手机中会自动掉用当前系统的安全键盘,比如小米、苹果,但是在一些特殊的机型中,会出现安全键盘收回后,其之前所占据的位置无法复原产生了空白,遮盖了页面。这个问题也是老生常谈了,具体处理我也不说明了,这次我想用一个新办法,既然出现安全键盘遮盖的问题,我就不让安全键盘出现记好了,抱着试验的方式,决定定下目标:

用文本框模拟出来密码框的输入形式

  1. 内容不明文显示
  2. 光标闪烁提示输入中
  3. 可以随意选择明文替代符号

1、使用css

这种方式很简单了,就一个样式,贼好用: -webkit-text-security

圆圈 -webkit-text-security: circle;

实心圆 -webkit-text-security: disc;

方块 -webkit-text-security: square;

明文 -webkit-text-security: none;

但是,能很明显看到前面的 -webkit-,这就怕是有兼容问题,我去查了一下

先看MDN的解释

再看Canisue的数据

感觉还马马虎虎,可以用。

2、使用js模拟

结构

html 复制代码
<template>
    <div class="login-container">
        <div class="form-container">
            <div class="password">
                <input type="text" v-model="password" autocomplete="off" @focus="psdFocusFn" @blur="psdBlurFn"
                       @click="psdClickFn" @input="setCursorPosition" @keydown="setCursorPosition" placeholder="密码">
                <div class="passwordMask" :class="{ blink: psdFocus }">{{ psdVal }}</div>
                <div v-show="password.length == 0" class="placeholder">密码</div>
            </div>
        </div>
    </div>
</template>

样式

css 复制代码
.login-container {
    position: relative;
    padding: 179px 16px 0;
    background-color: #ccc;
    height: 100vh;
    background: #1367FF;
    div {
        box-sizing: border-box;
    }
    .form-container {
        border-radius: 8px;
        background-color: #fff;
        padding: 16px;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        font-family: PingFangSC-Regular;
        font-size: 16px;
        font-weight: 400;
        .password {
            position: relative;
            input {
                width: calc(100% - 16px);
                height: 50px;
                background: #F5F7FC;
                border-radius: 8px;
                border: none;
                outline: none;
                padding-left: 16px;
                opacity: 0;
            }

            .passwordMask {
                position: absolute;
                left: 0;
                top: 0;
                padding-left: 16px;
                width: 100%;
                height: 100%;
                display: flex;
                align-items: center;
                pointer-events: none;
                font-weight: 400;
                font-size: 16px;
                pointer-events: none;
                display: flex;
                align-items: center;
                background: #F5F7FC;
                border-radius: 8px;
                &.blink {
                    &::after {
                        content: "|";
                        animation: blink steps(1) infinite 0.5s;
                    }
                }
            }
            .placeholder {
                color: #8E97A6;
                position: absolute;
                left: 16px;
                top: 0;
                width: calc(100% - 16px);
                height: 100%;
                display: flex;
                align-items: center;
                pointer-events: none;
                color: #8E97A6;
                font-weight: 400;
                font-size: 16px;
            }
        }
    }
}

这里解释一下,样式的做法,我的预想是使用文本框,不使用密码框,然后使用一个遮罩盒子完全覆盖在文本框的上方,用户看到的是上层的遮罩盒子,用户输入了多少个字符,就在遮罩盒子中放置多少个替换字符,就可以了,这种方式要解决以下几个问题:

1、不让用户看到文本框

css 复制代码
input {
    opacity: 0;
}

2、让用户能通过点击遮罩盒子文本框获取焦点

css 复制代码
.passwordMask {
    pointer-events: none;
}

3、有光标闪烁

这块使用一个|字符来模拟光标,让它动画出现、隐藏即可

定义动画

css 复制代码
@keyframes blink {
    0% {
        color: black;
    }

    50% {
        color: transparent;
    }

    100% {
        color: black;
    }
}

使用动画

css 复制代码
.passwordMask {
    &.blink {
        &::after {
            content: "|";
            animation: blink steps(1) infinite 0.5s;
        }
    }
}

在结构中设置,用户获取焦点时就添加blink

html 复制代码
<div class="passwordMask" :class="{ blink: psdFocus }">{{ psdVal }}</div>

逻辑

先上代码

js 复制代码
import { ref, watch } from 'vue'
const password = ref('')
const psdVal = ref('')
const psdFocus = ref(false)
const psdFocusFn = (e) => {
    psdFocus.value = true;
    setCursorPosition(e)
}
const psdBlurFn = () => {
    psdFocus.value = false;
}
const psdClickFn = (e) => {
    setCursorPosition(e)
}
const setCursorPosition = (e) => {
    e.target.focus();
    let ele = e.target;
    let len = ele.value.length || 0;
    if (document.selection) {
        let sel = ele.createTextRange();
        sel.moveStart('character', len);
        sel.collapse();
        sel.select();
    } else if (
        typeof ele.selectionStart == 'number' &&
        typeof ele.selectionEnd == 'number'
    ) {
        ele.selectionStart = ele.selectionEnd = len;
    }
}
watch(password, (newVal) => {
    psdVal.value = "*".repeat(newVal.length);
})

解释

1、通过一个变量psdFocus来记录是否获取了焦点

js 复制代码
import { ref } from 'vue'
const psdFocus = ref(false)
const psdFocusFn = (e) => {
    psdFocus.value = true;
}
const psdBlurFn = () => {
    psdFocus.value = false;
}

通过这个psdFocus变量来影响是否显示光标

html 复制代码
<div class="passwordMask" :class="{ blink: psdFocus }">{{ psdVal }}</div>

2、通过变量password记录输入数据,通过变量psdVal表示要显示的字符,当然这个字符可以随便设置

js 复制代码
import { ref, watch } from 'vue'
const password = ref('')
const psdVal = ref('')
watch(password, (newVal) => {
    psdVal.value = "*".repeat(newVal.length);
})

结合结构来看,在没有输入内容的时候显示提示文本

html 复制代码
<input type="text" v-model="password">
<div class="passwordMask" :class="{ blink: psdFocus }">{{ psdVal }}</div>
<div v-show="password.length == 0" class="placeholder">密码</div>

到这步后,基本逻辑就完成了

3、还有最后一步

还有一个很重要的问题,就是用户如果点击了中间位置,用户只能删除真实光标前面的字符,那么真实光标后面的字符就删除不了

怎么办?

(1)让假光标随时和真光标保持在同一位置

可以办到,我不想整了(JYM你们来吧,加油!)

(2)让假光标一直保持在最后

这个简单点,就这么干,让用户妥协一下

定义函数,调用时光标移到最后

js 复制代码
const setCursorPosition = (e) => {
    e.target.focus();
    let ele = e.target;
    let len = ele.value.length || 0;
    if (document.selection) {
        let sel = ele.createTextRange();
        sel.moveStart('character', len);
        sel.collapse();
        sel.select();
    } else if (
        typeof ele.selectionStart == 'number' &&
        typeof ele.selectionEnd == 'number'
    ) {
        ele.selectionStart = ele.selectionEnd = len;
    }
}

用户点击元素、获取焦点、输入、摁下键盘时调用setCursorPosition函数(饱和打击)

js 复制代码
const psdFocusFn = (e) => {
    setCursorPosition(e)
}
const psdClickFn = (e) => {
    setCursorPosition(e)
}
html 复制代码
<input type="text" @focus="psdFocusFn" @click="psdClickFn" @input="setCursorPosition" @keydown="setCursorPosition">

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端