这篇文章发布于 2021年11月14日,星期日,22:44,归类于 CSS相关, HTML相关。 阅读 37327 次, 今日 5 次 22 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10188 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
注册时候密码输入框显示密码强度是个很常见的交互效果,例如下面找的效果截图:
这种效果实现最好的方法一定是使用 HTML <meter>
元素,无论是跟随强度的 UI 色值变化,还是弱中强的文字提示,都是可以使用纯 CSS 实现,无需 JS 去控制 DOM 的色值与尺寸变化。
一、先看实现效果
下面两张图就是最终在浏览器中实现的效果。
Chrome 浏览器下:
Safari 浏览器下:
眼见为实,您可以狠狠的点击这里:meter元素与密码强度实现demo
接下来一步一步带大家了解具体的实现过程,先从了解 <meter>
元素开始。
二、meter 元素基本特性与效果
单词 meter 直译意思是“计量器”,“计量表”,因此,在 Web 中,任何与丈量相关,同时需要分阶段提示的场景都非常使用使用 <meter>
元素。
例如汽车油箱剩余油量,降雨量,风度,温度,游戏中人物角色的血量等。
先看下面一段代码:
生命值:<meter min="0" max="100" low="30" high="60" optimum="80" value="20"></meter> 生命值:<meter min="0" max="100" low="30" high="60" optimum="80" value="50"></meter> 生命值:<meter min="0" max="100" low="30" high="60" optimum="80" value="70"></meter>
在现代浏览器下就会有如下图所示的 UI 效果。
下面则是实时渲染效果,大家可以看下自己当前访问设备中的效果:
生命值:
生命值:
生命值:
其中出现了 6 个 HTML 属性,正好涵盖了 <meter>
元素所有常用属性。
属性简介
min
和max
属性表示数值范围,默认是 0-1,也就是如果min
属性如果不设置,则认为是 0,max
属性不设置则认为是 1。value
表示当前的值,默认是 0。low
和high
是比较特殊的 HTML 属性,目前仅出现在<meter>
元素上,表示警戒值,其中low
表示过低警戒值,high
表示过高警戒值。但是,大家务必注意,过低还是过高是否需要警戒还需要一个属性进行判定,那就是
optimum
属性。optimum
属性表示最佳值,用来决定过低和过高值属于正常还是异常,这个属性很重要,也是<meter>
元素学习的难点。
optimum 属性
optimum 属性的作用表现描述如下:
- 如果
optimum
属性值在low
和high
之间,则说明low
和high
都是警戒值,只有在这个区间范围的值才是正常的,因此,最终的色值状态只会有两个,即橙色警戒和绿色正常。代码示意:
生命值:<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>
效果如下图所示,最多只会出现两种颜色。
-
如果
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 效果了。
然而,事情并没有预想的那么简单。
- 密码强度如何确定?
- 表示 3 个颜色的伪元素最多只会出现 1 个,而我们的目标效果却是 3 色同时显示,另外,默认的色值是连续的,而需要的效果是分段的,该怎么实现呢?
- 强度色块下面还有“弱 中 强”的文字提示,这个布局效果又该如何实现呢?
关于这些实现难点,我们一个一个来看下,其中不乏一些有创意的实现技巧。
//zxx: 如果你看到这段文字,说明你现在访问是不是原文站点,更好的阅读体验在这里:https://www.zhangxinxu.com/wordpress/?p=10188(作者张鑫旭)
四、密码强度的计算
密码强度的计算直接使用开源的公认的算法就可以了,比方说 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 好了。
五、最终样式实现的细节
1. 背景条的样式
首先,我们确定个尺寸,假设 长度是 120px,高度是 12px:
::-webkit-meter-bar { width: 120px; height: 12px; }
浏览器默认的边框效果是不需要的,然后为了兼容性,需要统一背景色,于是有:
::-webkit-meter-bar { width: 120px; height: 12px; border: 0; background: #eee; }
Firefox 浏览器可以使用 ::-moz-meter-bar 伪元素设置。
2. 色带的实现
浏览器默认的状态都是纯色,从左纯到右,想要变成一个一个的色带,不少人的想法会是叠加,也就是几个 <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 渐变重新实现之后,就是 红-橙-绿 三段,很好地模拟了三态强度效果。
最终的效果可以参见下面的 GIF 录屏。
3. 中间镂空分隔
上面的 GIF 效果,各个色块是紧密相连的,但是最终需要的效果是彼此之间有间隙,这个该如何实现呢?
方法其实挺多的,我这里选择使用遮罩实现,在色块的容器元素上绘制一个镂空渐变作为遮罩图像:
::-webkit-meter-bar { -webkit-mask: linear-gradient(to right, red 39px, transparent 0 41px, orange 0 79px, transparent 0 81px, green 0 ); }
此时,效果就会是这样:
4. 文字左中右对齐实现
接下来就是“弱中强”三个文字的效果实现了,如下图:
有人会想到使用一个 <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 行显示,从而让第一行的 “弱中强”三个字两端对齐。
效果如下所示:
我猜,有人会说,不对啊,“弱”和“强”两个字需要在色块的中间啊,不是边上。
对,边上是不对的,所以,需要修改下 left
和 top
位置微调下(中文字符占据宽度是 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; }
效果为:
接下来,就是文字变色的实现了。
5. 不同文字不同颜色实现
同一段文字显示不同颜色的技术其实很多很多年前(10年前)就介绍过了,详见:“CSS3下的渐变文字效果实现”,在《CSS新世界》这本书中也有介绍。
这里使用 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; }
再加上一些尺寸和位置的细节调整,于是就有了最终的效果:
6. Safari 浏览器
好,Chrome 浏览器搞定了,看看 Safari 浏览器下的效果,结果……呃……:
本以为是safari浏览器不支持,后来研究了一下,发现原来只需要设置外面的meter 元素边框尺寸为0或none就可以触发伪元素重定义了,也就是:
meter { border: 0; }
所有的效果就会出现,如下所示:
7. 使用 CSS 变量优化
最后把上面所有的实现整合一下,同时使用css变量优化一下渐变以及尺寸。就会得到如下所示的终极代码:
meter { --size: 60px; --gradient: linear-gradient(to right, red calc(var(--size) - 1px), transparent 0 calc(var(--size) + 1px), orange 0 calc(var(--size) * 2 - 1px), transparent 0 calc(var(--size) * 2 + 1px), green 0); width: calc(3 * var(--size)); border: 0; /* Safari */ position: relative; } meter::after { content: '弱 中 强 aaaaaaaaaaaaaaaaaaaaaa'; position: absolute; font-size: 14px; line-height: 20px; height: 20px; overflow: hidden; left: calc(var(--size) / 2 - .5em); right: calc(var(--size) / 2 - .5em); text-align: justify; -webkit-text-fill-color: transparent; background: var(--gradient) calc(.5em - var(--size) / 2) / calc(3 * var(--size)); -webkit-background-clip: text; } ::-webkit-meter-bar { height: 12px; width: calc(3 * var(--size)); border: 0; background: #eee; -webkit-mask: var(--gradient); mask: var(--gradient); } ::-webkit-meter-even-less-good-value { background: red; } ::-webkit-meter-suboptimum-value { background: linear-gradient(to right, red var(--size), orange 0); } ::-webkit-meter-optimum-value { background: linear-gradient(to right, red var(--size), orange 0 calc(2 * var(--size)), green 0); }
如果想要改变强度测量条元素的宽度,只需要修改CSS自定义属性 --size
的值就可以了。
眼见为实,效果体验
您可以狠狠地点击这里:meter元素自定义效果纯享版demo
进行效果体验。
至此,所有相关实现的介绍都结束了。
六、总结
当然,正如一开始所提到的,<meter>
元素不仅可以用来表示密码强度。在 Web 中,所有与丈量、测量相关的场景,都可以考虑使用这一个 HTML 元素。
有人可能会纠结 IE 浏览器的问题。
毕竟 <meter>
元素的兼容性是这样子的。
一种方法是使用polyfill,我这里找了个项目:https://github.com/fisker/meter-polyfill
这个项目的作者可能是国人,因为头像看起来非常熟悉,不过这个 polyfill 项目我自己没试过,所以质量如何并不清楚。
从我自己的角度讲,如果我是遇到这样的场景,我会说服产品直接放弃 IE浏览器,就不用搞什么所谓的密码强度了,本来这种密码强度就是锦上添花的功能,没有的话,也不影响功能。
况且现在 IE 浏览器占比其实非常低了,3%都不到,我不知道其他公司产品怎样的,以及我们开发产品总是要面向未来的,你不能说我当下还有一些 IE 浏览器用户,你就放弃了未来,想想看,现在项目中还有兼容 IE8 浏览器的代码,是不是想吐。
当然,如果产品是个直溜子说服不通,以为工程师就是想偷懒。
那我觉得专门给IE做一个特殊的处理,其实也不需要多大的成本,回头哪天 IE 嗝屁了直接移除就好。
好,巴拉巴拉,断断续续,没想到讲了这么多,这篇文章篇幅又铺出来了,所以就不再啰嗦了。
行文不易,欢迎分享,感谢转发!
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=10188
(本篇完)
- 伪元素表单控件默认样式重置与自定义大全 (0.565)
- HTML input type=file文件选择表单元素二三事 (0.213)
- CSS ::file-selector-button伪元素简介 (0.139)
- 说说HTML5中label标签的可访问性问题 (0.128)
- CSS radio/checkbox单复选框元素显隐技术 (0.128)
- HTML5 placeholder实际应用经验分享及拓展 (0.128)
- 小tip:CSS计数器+伪类实现数值动态计算与呈现 (0.128)
- CSS :focus伪类JS focus事件提高网站键盘可访问性 (0.128)
- 突发奇想,同步单复选框checked态岂不点击通杀? (0.128)
- 复选框单选框与文字对齐问题的研究与解决 (0.104)
- CSS3&HTML5各浏览器支持情况一览表 (RANDOM - 0.054)
demo 在火狐浏览器里显示异常,例如 101.0 版本。
我们公司产品用户还有使用XP 和win5的,?
我就吐槽一下现在互联网企业,天天要求复杂的密码,那么多网站,我那里记得了这么多,你的业务对于我来不重要。输入一个密码烦死了。 实在不记得了 找回还不能设置之前设置过得密码。又要想一个新密码 哎
手机号登陆或者第三方授权登陆,可以作为优选方案了
大佬,optimum 属性 第二点的代码value都是20,是不是没改啊
感谢反馈,改了前面,没注意后面的 value 属性
–size大于105px时,aaaaa会露出来,文字颜色也不对。。。
可以看看我改了之后的
https://codepen.io/pandamo-Code/pen/pormWZy
主要修改 meter::after
去掉:
left: calc(var(–size) / 2 – .5em);
right: calc(var(–size) / 2 – .5em);
text-align: justify;
修改:
background: var(–gradient) calc(.5em – var(–size) / 2) / calc(3 * var(–size));
为
background: var(–gradient);
增加:
width: calc(3 * var(–size));
text-align-last: justify;
padding: 0 calc(0.4 * var(–size));
box-sizing: border-box;
思路不错,如果不需要兼容 Safari 是更好的实现。
大佬们,我想问下这种::-webkit的属性是具体在哪里可以查得到啊
打开控制台设置 勾选Show user agent shadow DOM,然后再看CSS调试
optimum 属性介绍, 第二个演示代码好像是错的, value 值应该没改
给大佬点赞,真是能玩出花来,太棒了
我看不懂,但深感震撼!?
最后的“行为不易”应该是“行文不易”嘛?
感谢反馈~
text-align-end: justify;
行末两段对齐。
大为震撼
第一次见,学到了
最后的那个demo在firefox中打开样式还是有问题,没有分隔条,不显示下面的字。
firefox 并不支持伪元素,也不支持基于不同状态的样式自定义
firefox有自己的伪类
::-moz-meter-bar
路过,一赞!