使用meter元素实现密码强度效果(含密码强度计算算法)

9959次阅读 356人点赞 作者: WuBin 发布时间: 2022-01-06 14:23:15
扫码到手机查看

实现效果

注册时候密码输入框显示密码强度是个很常见的交互效果,这种效果实现最好的方法一定是使用 HTML<meter>元素,无论是跟随强度的 UI 色值变化,还是弱中强的文字提示,都是可以使用纯 CSS 实现,无需 JS 去控制 DOM 的色值与尺寸变化。

先看最终效果:http://code.wubin.work/code/html/meter-password-strong

接下来一步一步带大家了解具体的实现过程,先从了解<meter>元素开始。

meter 元素基本特性与效果

单词 meter 直译意思是“计量器”,“计量表”,因此,在 Web 中,任何与丈量相关,同时需要分阶段提示的场景都非常使用使用<meter>元素。

例如汽车油箱剩余油量,降雨量,风度,温度,游戏中人物角色的血量等。

先看下面一段代码:

生命值:<meter min="0" max="100" low="30" hight="60" optimum="80" value="20"></meter>
生命值:<meter min="0" max="100" low="30" hight="60" optimum="80" value="50"></meter>
生命值:<meter min="0" max="100" low="30" hight="60" optimum="80" value="70"></meter>

点击查看示例:http://code.wubin.work/code/html/meter-password-strong/test1.html

其中出现了 6 个 HTML 属性,正好涵盖了<meter>元素所有常用属性。

属性简介

  • minmax属性表示数值范围,默认是 0-1,也就是如果min属性如果不设置,则认为是 0,max属性不设置则认为是 1。
  • value表示当前的值,默认是 0。
  • lowhigh是比较特殊的 HTML 属性,目前仅出现在<meter>元素上,表示警戒值,其中low表示过低警戒值,high表示过高警戒值。

    但是,大家务必注意,过低还是过高是否需要警戒还需要一个属性进行判定,那就是optimum属性。

  • optimum属性表示最佳值,用来决定过低和过高值属于正常还是异常,这个属性很重要,也是<meter>元素学习的难点。

optimum 属性

optimum 属性的作用表现描述如下:

如果optimum属性值在lowhigh之间,则说明lowhigh都是警戒值,只有在这个区间范围的值才是正常的,因此,最终的色值状态只会有两个,即橙色警戒和绿色正常。

代码示意:

生命值:<meter max="100" low="30" high="60" optimum="50" value="20"></meter>
生命值:<meter max="100" low="30" high="60" optimum="50" value="50"></meter>
生命值:<meter max="100" low="30" high="60" optimum="50" value="70"></meter>

效果如下所示,最多只会出现两种颜色。

http://code.wubin.work/code/html/meter-password-strong/test1.html

如果optimum属性值比low还要小,则说明 low 并不是警戒线,反而是推荐线,也就是越小反而越高,此时,low-high 之间的范围就属于警戒,而超过 high 的值就属于危险了。

于是,最终<meter>元素可以有 3 段色值状态,HTML 代码示意:

生命值:<meter max="100" low="30" high="60" optimum="20" value="20"></meter>
生命值:<meter max="100" low="30" high="60" optimum="20" value="50"></meter>
生命值:<meter max="100" low="30" high="60" optimum="20" value="70"></meter>
如果optimum属性值比high还要大,也会出现 3 段色值状态,此时,大于 high 的值会被认为是正常的,因此表现为绿色,这个效果,就是一开始那个例子所演示的那个效果,代码和截图效果是这样的:
生命值:<meter max="100" low="30" high="60" optimum="80" value="20"></meter>
生命值:<meter max="100" low="30" high="60" optimum="80" value="50"></meter>
生命值:<meter max="100" low="30" high="60" optimum="80" value="70"></meter>

回到本文这里,由于密码强度越强越好,因此,很显然,需要设置optimum属性值比high还要大。

但是,我们需要的是明显分段的效果,而浏览器默认的<meter>元素效果就是个色条,不符合产品需求,那有没有什么办法自定义 UI 呢?

meter 元素的样式自定义

在现代浏览器下,<meter>元素提供了若干伪元素,可以让我们对<meter>元素进行样式自定义。

其中,Chrome 浏览器和 Safari 浏览器可以使用的伪元素非常丰富,Firefox 浏览器相对少一些。

这里,主要以 Chrome 浏览器示意。

所有可以设置<meter>元素的选择器包括下面这些:

  • meter 元素自身选择器;
  • ::-webkit-meter-inner-element {}
  • ::-webkit-meter-bar {} 灰色背景条。
  • ::-webkit-meter-even-less-good-value {} 红色。
  • ::-webkit-meter-optimum-value {} 橙色。
  • ::-webkit-meter-suboptimum-value {} 绿色。

各个伪元素所对应的 DOM 层级结构如下图所示:

使用字符图形表示是这样的:

meter
  |-- ::-webkit-meter-inner-element
    |-- ::-webkit-meter-bar
      |-- ::-webkit-meter-even-less-good-value  -|
      |-- ::-webkit-meter-suboptimum-value      -|--> 只会同时出现1|-- ::-webkit-meter-optimum-value         -|

所以,我们就可以使用上面的伪元素实现我们想要的 UI 效果了。

然而,事情并没有预想的那么简单。

  1. 密码强度如何确定?
  2. 表示 3 个颜色的伪元素最多只会出现 1 个,而我们的目标效果却是 3 色同时显示,另外,默认的色值是连续的,而需要的效果是分段的,该怎么实现呢?
  3. 强度色块下面还有“弱 中 强”的文字提示,这个布局效果又该如何实现呢?

关于这些实现难点,我们一个一个来看下,其中不乏一些有创意的实现技巧。

密码强度的计算

密码强度的计算直接使用开源的公认的算法就可以了,比方说 

zxcvbn:https://github.com/dropbox/zxcvbn

使用也相当简单,引入然后执行,示意:

<script src="https://cdn.bootcdn.net/ajax/libs/zxcvbn/4.4.2/zxcvbn.js"></script>
<script>
    let objResult = zxcvbn('your password');
</script>

其中 objResult 包含下面这些属性:

{
    guesses: Number
    guesses_log10: Number
    score: Number (0-4)
    crack_times_seconds: Object (不同运算力下的破解时间,数值展示)
    crack_times_display: Object (不同运算力下的破解时间,字符描述展示)
    sequence: Array 验证序列
    feedback: 反馈与建议
    calc_time: zxcvbn 计算此密码强度花费的时间,单位是毫秒 ms
}

其中,与密码强度直接相关的是:guesses、guesses_log10 和 score。

其中,score 范围是 0-4,数值越大,密码越安全,虽然也能解决强度的判断,但是体现在<meter>元素上就没有了丰度的 value 变化,视觉效果也不咋地。

因此,在本文所描述的场景下,我们使用 guesses_log10 作为我们的强度判断值。

根据我的测试,guesses_log10 的值在 12 以上,密码强度就已经很 Strong 了,考虑到弱中强三种状态的色条范围是三等分的,因此,最终的<meter>元素的高低值如下所示:

<meter min="0" max="12" low="4" high="8" optimum="10"></meter>

相关 JS 代码则是这样子的:

meter.value = zxcvbn('paddworld').guesses_log10;

剩下的视觉表现的工作全部都交给 CSS 好了。

最终样式实现的细节

背景条的样式

首先,我们确定个尺寸,假设 长度是 120px,高度是 12px:

::-webkit-meter-bar {
    width: 120px;
    height: 12px;
}

浏览器默认的边框效果是不需要的,然后为了兼容性,需要统一背景色,于是有:

::-webkit-meter-bar {
    width: 120px; height: 12px;
    border: 0;
    background: #eee;
}

Firefox 浏览器可以使用 ::-moz-meter-bar 伪元素设置。

色带的实现

浏览器默认的状态都是纯色,从左纯到右,想要变成一个一个的色带,不少人的想法会是叠加,也就是几个<meter>元素叠加在一起,其实不需要这么麻烦的,我们使用 CSS 渐变模拟就好了。

例如,进入橙色状态的时候,把前 1/3 部分改成红色(原来是橙色),这样,当状态条从红色变成橙色的时候,就像是无缝添加的,而不是突然替换,绿色这部分也是类似的。

代码示意:

::-webkit-meter-even-less-good-value {
    background: red;
}
::-webkit-meter-suboptimum-value {
    background: linear-gradient(to right, red 40px, orange 0);
}
::-webkit-meter-optimum-value {
    background: linear-gradient(to right, red 40px, orange 0 80px, green 0);
}

::-webkit-meter-optimum-value伪元素举例,浏览器默认状态下,这个伪元素是纯绿色,这里使用 CSS 渐变重新实现之后,就是 红-橙-绿 三段,很好地模拟了三态强度效果。

中间镂空分隔

上面的效果,各个色块是紧密相连的,但是最终需要的效果是彼此之间有间隙,这个该如何实现呢?

方法其实挺多的,我这里选择使用遮罩实现,在色块的容器元素上绘制一个镂空渐变作为遮罩图像:

::-webkit-meter-bar {
    -webkit-mask: linear-gradient(to right, 
      red 39px, 
      transparent 0 41px, 
      orange 0 79px, 
      transparent 0 81px, 
      green 0
    );
}

文字左中右对齐实现

接下来就是“弱中强”三个文字的效果实现了。

有人会想到使用一个<span>元素和<meter>元素包在一起再重定位,这个虽然也能实现,但是 HTML 就啰嗦了,以后也不太好维护。

实际上,直接使用<meter>元素就可以实现,方法就是::after伪元素。

meter::after {
    content: '弱中强';
}

不过有几个问题,首先,文字占据的尺寸空间,影响了和输入框的垂直对齐,这个好办,设置为绝对定位就可以了:

meter::after {
    content: '弱中强';
    position: absolute;
}

但是,“弱中强”三个字要分别在 3 个颜色片段的中间,这个对齐该怎么实现呢?

我们可以让伪元素宽度充满<meter>元素,给文字中间增加空格、末尾增加一串长长的连续英文字符实现:

meter {
    position: relative;    
}
meter::after {
    content: '弱 中 强 aaaaaaaaaaaaaaaaaaaaaa';
    position: absolute;
    left: 0; right: 0;
    /* 两端对齐 */
    text-align: justify;
    /* 隐藏 aaaaaaaaaaaaaaaaaaaaaa */
    height: 20px;
    line-height: 20px;
    overflow: hidden;
}

为什么后面需要一段莫名其妙的'aaaaaaaaaaaaaaaaaaaaaa'呢,因为两端对齐效果的实现,处理字符之间有空格,还需要内容超过1行,而连续英文字符默认是不会换行的,因此会作为整体在第 2 行显示,从而让第一行的 “弱中强”三个字两端对齐。

但是如此文字就会在左边,我们要求的是在中间位置。

所以,需要修改下lefttop位置微调下(中文字符占据宽度是 1em):

meter {
    position: relative;    
}
meter::after {
    content: '弱 中 强 aaaaaaaaaaaaaaaaaaaaaa';
    position: absolute;
    left: calc(20px - .5em);
    right: calc(20px - .5em);
    /* 两端对齐 */
    text-align: justify;
    /* 隐藏 aaaaaaaaaaaaaaaaaaaaaa */
    height: 20px;
    line-height: 20px;
    overflow: hidden;
}

不同文字不同颜色实现

详见:“CSS3下的渐变文字效果实现”。这里使用background-clip:text加 linear-gradient 渐变背景实现的。

meter::after {
    -webkit-text-fill-color: transparent;
    background-image: linear-gradient(to right, 
        red 39px, 
        transparent 0 41px, 
        orange 0 79px, 
        transparent 0 81px, 
        green 0
    );
    -webkit-background-clip: text;
}

Safari 浏览器

原本以为safari浏览器不支持,后来研究了一下,发现原来只需要设置外面的meter 元素边框尺寸为0或none就可以触发伪元素重定义了,也就是:

meter {
    border: 0;
}

最终,使用css变量优化一下渐变以及尺寸,可以查看最终结果:

http://code.wubin.work/code/html/meter-password-strong/final.html

兼容性

当然,正如一开始所提到的,<meter>元素不仅可以用来表示密码强度。在 Web 中,所有与丈量、测量相关的场景,都可以考虑使用这一个 HTML 元素。

有人可能会纠结 IE 浏览器的问题。

毕竟<meter>元素的兼容性是这样子的:

https://caniuse.com/?search=meter

一种方法是使用polyfill,我这里找了个项目:https://github.com/fisker/meter-polyfill

不过这个 polyfill 项目我自己没试过,所以质量如何并不清楚。

从我自己的角度讲,如果我是遇到这样的场景,我会说服产品直接放弃 IE浏览器,就不用搞什么所谓的密码强度了,本来这种密码强度就是锦上添花的功能,没有的话,也不影响功能。

况且现在 IE 浏览器占比其实非常低了,3%都不到,我不知道其他公司产品怎样的,以及我们开发产品总是要面向未来的,你不能说我当下还有一些 IE 浏览器用户,你就放弃了未来,想想看,现在项目中还有兼容 IE8 浏览器的代码,是不是想吐。

当然,如果产品是个直溜子说服不通,以为工程师就是想偷懒。

那我觉得专门给IE做一个特殊的处理,其实也不需要多大的成本,回头哪天 IE 嗝屁了直接移除就好。

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:meter
推荐阅读
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    10049次阅读 659人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • Vue

    盘点Vue2和Vue3的10种组件通信方式

    Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异;本文将通过选项式API组合式API以及setup三种不同实现方式全面介绍Vue2和Vue3的组件通信方式。

    4675次阅读 346人点赞 发布时间: 2022-08-19 09:40:16 立即查看
  • JS

    几个高级前端常用的API

    推荐4个前端开发中常用的高端API,分别是MutationObserver、IntersectionObserver、getComputedstyle、getBoundingClientRect、requ...

    14754次阅读 967人点赞 发布时间: 2021-11-11 09:39:54 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    13961次阅读 525人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    12703次阅读 1023人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    16939次阅读 1357人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    15465次阅读 1257人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    30556次阅读 2403人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    26526次阅读 2041人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录