8

震惊,type="range" 居然可以实现评星功能

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzU4MzUzODc3Nw%3D%3D&%3Bmid=2247484221&%3Bidx=1&%3Bsn=120ccd011fca8817503a729c8862bf78
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2mA3Anv.png!mobile

这是一个不断探究再探究的小故事。

为啥会有用css实现评星想法?

vy2EZnY.gif!mobile

像上面这种评星是一种很常见的需求,我原来是借助JS实现的,没有遇到多大的挑战。

后来 张老师 [1]code review 我这段代码的时候,顺便提了一嘴 type="range" 应该可以实现,当时的我想破脑袋也想不出来,脑子是蒙的。

因为是自己写这种评星的功能,所以封装了一下,方便下次使用,就按照日常的习惯写了篇博客 js 实现评分组件 [2] ,事情本来就这么过去了,可是后来 同事文彬 [3] 无意间看到了我的博客,觉得我的实现方式不优雅,和我聊了几句:

6VziArJ.jpg!mobile

后来我根据文彬的建议,继续优化我的代码,我很开心的和他说我把代码进行了优化,效果很不错,于是我把优化以后的代码发给他看。可他却用纯 css 实现了,真的是道高一尺魔高一丈。

rYFVvu2.png!mobile

震惊!居然就是张老师一开始顺嘴一提的type="range"实现的。这也太厉害了吧,只有你想不到,没有你实现不了的。我们先看看来实现效果

在线demotype=range 实现评星功能 [4]

如何 type="range" 用评星功能

input type="range" 只是一个简单的元素吗?

不是的,他是一个组件,在DOM结构里面有一种特殊的 DOM 叫 shadow DOM

Qn6zMjY.jpg!mobile

通过浏览器控制台可以发现原来 input type="range" 元素里面是包含了其他的元素的,只是平常的时候我们见不着,他是被隐藏起来,没有设置特定的选项,我们看不见他。

那我们怎么让他显示出来???

可以在 Chrome 中开启 shadow-dom 的显示,方式为在控制台右上角 setting > preferences > Elements ,勾选 Show user agent shadow DOM (已经勾选过的可以忽略)

打开那个控制开关之后,我们就发现 input type="range" 标签下面有一个 shadow-root 节点。节点里面就有很多的 DOM 节点,再一次证实了 input type="range" 并不是一个标签,而是一个组件,只是这个组件不容易被发现。

继续探究如何自定义 input type="range" 的样式

在元素审查的时候,我们发现了一个东西

NzQRjyb.png!mobile

既然可以定位到dom,还能看到样式,那修改他是不是有效果呢?试一试,或许就成功了呢。

z2QnumE.gif!mobile

fA77niE.gif!mobile

咦,成功了耶!

原来那两个伪元素是可以用来修改元素的样式:

::-webkit-slider-runnable-track [5] :表示其在type为range的input标签内中的滑块凹槽,也就是滑块可滑动的区域(简单说就是滑块凹槽区域的样式)

::-webkit-slider-thumb [6] :这是type为range的input标签内的一种伪类样式,用于设置range的滑块的具体样式,该伪类只在内核为webkit/blink的浏览器中有效(简单说就是滑块区域的剩下的部分)

接下来的问题就是,如何把星星当背景且数量可控制的覆盖上去呢?

脑子灵感一闪 客栈说书:CSS遮罩CSS3 mask/masks详细介绍 [7] 里面提到了把图案遮罩在背景之上,尝试一下嘛,说不定有收获呢。

QnyIVv3.gif!mobile

到此完美实现 input type="range" 的评星功能,并且图片颜色随着背景的变化而变化 => 图案颜色随着背景颜色自由变化

M3YBriu.gif!mobile

在线demo的 type=range 实现评分 [8]

核心代码:

input[type="range"]{
-webkit-appearance: none; //去掉浏览器自带的样式
width: 100px;
margin: 0;
outline: 0;
}
input[type="range" i]::-webkit-slider-runnable-track {
background: coral;// 设置五角星的颜色
height: 20px;
-webkit-mask: url("data:image/svg+xml,%3Csvg width='12' height='11' viewBox='0 0 12 11' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0l1.693 3.67 4.013.476L8.74 6.89l.788 3.964L6 8.88l-3.527 1.974.788-3.964L.294 4.146l4.013-.476L6 0z' fill='%23F67600'/%3E%3C/svg%3E"); // 遮罩的图案,可以任意定制
-webkit-mask-size: 20px;// 遮罩图片的大小
-webkit-mask-repeat: repeat-x;
}
input[type="range" i]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 0;
height: 100%;
box-shadow: 999px 0px 0px 999px #E8EAED;
}

css 实现了,评星功能,代码量少,且灵活。

兼容性怎么样?还能再继续完善吗?

只要兼容 input type="range" 应该都可以实现。

在测试火狐浏览器的时候,发现在这个评星挂了。

BZrmay.png!mobile

我继续和文彬讨论,如何解决这个兼容性问题:

i2uQzaa.png!mobile

兼容火狐浏览器,谷歌下失效 [9]

核心实现代码

input[type=range]{
-webkit-mask: url("data:image/svg+xml,%3Csvg width='12' height='11' viewBox='0 0 12 11' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0l1.693 3.67 4.013.476L8.74 6.89l.788 3.964L6 8.88l-3.527 1.974.788-3.964L.294 4.146l4.013-.476L6 0z' fill='%23F67600'/%3E%3C/svg%3E");
-webkit-mask-size: 20px;
-webkit-mask-repeat: repeat-x;
height: 20px;
}
input[type=range]::-moz-range-track{
background: #E8EAED;
height: inherit;
}
input[type=range]::-moz-range-progress {
background: coral;
height: inherit;
}
input[type=range]::-moz-range-thumb {
width: 0;
opacity: 0;
}

冰冻三尺非一日之寒,用 input type="range" 实现这个功能并非一朝一夕,没有一番苦功夫,是不可能的。

fyUN7vz.jpg!mobile

这个实现很不错,于是考虑是否可以加到开源 LuLu UI [10] 中去,但是在开源 UI 里面考虑的情况就多了很多,比如:如果不是5颗星星,用户要自定义xx个星星,用户怎么改?评分范围不是0-5,而是0-100...遇到的难题不少,差点我就放弃了,多亏了同事的继续指点:

326jYny.jpg!mobile

整合后的最新代码,就期待最新主题的 LuLu UI [11] 吧 ,预计年中对外,哈哈!

总结

我的同事们脑子里装一定是的百科全书,仿佛就没有他们不知道的东西。好几次都找同事们,帮忙看问题解决问题,他们的脑袋转悠转悠,贼快的给出了我要的答案,那速度比谷歌还快。真开心,真羡慕自己能够拥有一群这样的大佬同事。以后做需求写功能都可以像他们取经,是否有更好的实现方式,交流讨论,然后成长,选择比努力更重要。

年底了,你们不用羡慕我,你值得拥有,来试一试我公司吧, 先了解一下团队的日常活动 [12] ,每个月都有活动哟,快来和我一起尽情工作、拼命玩

无论你的技术栈偏前还是偏后,都有合适你的位置,可以年前面试年后入职,欢迎简历至 [email protected] [13]

工作地点:上海浦东微电子港,本科以上学历,2年以上工作经验,简历必回。

References

[1] 张老师:  https://www.zhangxinxu.com/wordpress/

[2] js 实现评分组件:  https://sunseekers.github.io/2020/12/12/star/

[3] 同事文彬:  https://github.com/XboxYan

[4] 在线demotype=range 实现评星功能:  https://codepen.io/qingchuang/pen/jOMmQab

[5] ::-webkit-slider-runnable-track:  https://developer.mozilla.org/zh-CN/docs/Web/CSS/::-webkit-slider-runnable-track

[6] ::-webkit-slider-thumb:  https://developer.mozilla.org/zh-CN/docs/Web/CSS/::-webkit-slider-thumb

[7] 客栈说书:CSS遮罩CSS3 mask/masks详细介绍:  https://www.zhangxinxu.com/wordpress/2017/11/css-css3-mask-masks/

[8] 在线demo的 type=range 实现评分:  https://codepen.io/qingchuang/pen/jOMmQab

[9] 兼容火狐浏览器,谷歌下失效:  https://codepen.io/qingchuang/pen/VwKVJYL

[10] LuLu UI:  https://github.com/yued-fe/lulu

[11] LuLu UI:  https://github.com/yued-fe/lulu

[12] 先了解一下团队的日常活动:  https://story.yux.team/#&202012

[13] [email protected]mailto:[email protected]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK