使用文本框模拟密码框

前景提要

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

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

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

相关推荐
Heidi__19 分钟前
Vue 3 的响应式原理
前端·javascript·vue.js
LinXunFeng28 分钟前
Flutter - Xcode16 还原编译速度
前端·flutter·xcode
夏之小星星28 分钟前
element-ui自制树形穿梭框
前端·javascript·ui·elementui·vue
zhangivon1 小时前
如何设计灵活可扩展的前端日志解决方案:提升应用稳定性与可观测性
前端
程序员黄同学1 小时前
解释观察者模式,如何实现观察者模式?
前端·算法·观察者模式
观无2 小时前
JWT认证服务
前端·c#·vue
匹马夕阳2 小时前
(一)前端程序员转安卓开发分析和规划建议
android·前端
Monly213 小时前
Vue:Table在点击删除的时候阻止行点击事件
前端·javascript·vue.js
我自纵横20233 小时前
使用 JavaScript 动态设置 CSS 样式
开发语言·前端·javascript·css·html·json·html5
Z编程3 小时前
纯css实现环形进度条
前端·css