Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

纯CSS实现网站常用的五角星评分和分数展示交互效果 #8

Open
jawil opened this issue Mar 30, 2017 · 8 comments
Open

Comments

@jawil
Copy link
Owner

jawil commented Mar 30, 2017

最近做的一个项目涉及到评分和展示分数的模块,UI设计师也给了几个切好的图片,实现五角星评分方式很多,本质爱折腾的精神和对性能追求以及便于维护的考虑,搜集和尝试了很多方式,最终采用了纯css驱动的实现方式完成评分和展示分数的功能,没有js,也就意味着没判断逻辑,代码出错的几率更少,也更便于维护,在此,把这个功能的实现的过程记录和分享一下,一起学习交流。

原文收录在我的 GitHub博客 (https://github.com/jawil/blog) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步。

五角星的实现

1.图片或者字体图标

不极致追求性能的话,直接利用设计师给的png或者jpg啥的,或者直接转成base64.

2:利用Fontawesome 图标库,其实只是一种矢量字体。

HTML:

<div class="icon"></div>

CSS:

@import url(http://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css);

.icon:before {
    content: '\f005';
    font-family: FontAwesome;
}

在线预览地址

3.利用css3描绘拼凑一个五角星。

基本原理:利用transparent的透明不可见和transform转换拼接一个正五角星。

HTML:

<div class="star-five"></div>

CSS:

.star-five{
width: 0;
height: 0;
color: red;
margin: 50px 0;
position: relative;
display: block;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
transform:rotate(35deg);
}

.star-five:before{
width: 0;
height: 0;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
border-bottom: 80px solid red;
position: absolute;
top: -45px;
left: -65px;
color: white;
display: block;
content: "";
transform:rotate(-35deg);
}
.star-five:after{
width: 0;
height: 0;
display: block;
position: absolute;
color: red;
top: 3px;
left: -105px;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
content: "";
transform:rotate(-70deg);
}

在线预览地址

不建议使用这种,因为选择之后改变颜色状态比较麻烦,改起来很不方便,不如前面几种方便好维护。

4.直接使用五角星符号

★😄

简单粗暴,容易控制,品相协调,下面实现方式以★为准。




关于CSS的一些选择器

不用js来控制评分,当然不能错过强大的css选择器,这里就先介绍一下关于实现这个功能要用到的一些css选择器。

在介绍css强大的选择器之前,先普及一下“CSS radio/checkbox单复选框元素显隐技术”,又称“checkbox hack技术”。

1.checkbox hack技术

我们使用CSS一些特殊的选择器,然后配合单选框以及复选框自带的一些特性,可以实现元素的显示隐藏效果。然后通过一
些简单的扩展,我们可以不使用任何JavaScript代码实现类似:自定义的单复选框,“更多”展开与收起效果,选项卡切换
效果,或是多级下拉列表效果等等。

相信很多前端开发人员都会遇到boss让修改checkbox和radio样式,毕竟自带的样式太丑了。后来我们发现修改自带样式
并不是那么容易,最后直接使出杀手锏——点击之后替换图片。
今天教大家一种方法,不用替换图片,随意修改样式。还是先看效果图:

先讲一下原理:两个关键东东,一是伪类选择器:checked,表示对应控件元素(单选框或是复选框)选中时的样式;二就是加号+ 相邻兄弟选择器,这个符号表示选择后面的兄弟节点。于是,两者配合,就可以轻松自如控制后面元素的显示或者隐藏,或是其他样式了。 而如何让单复选框选中和不选中了,那就是label标签了哈,for属性锚定对应的单选框或是复选框,然后点击这里的label标签元素的时候,对应的单复选框就会选中或是取消选中。然后,就有上面的效果啦!

这里只给一个radio单选框的代码,仅供参考:

HTML:

<div class="radio-beauty-container">
    <label>
        <span class="radio-name">前端工程师</span>
        <input type="radio" name="radioName" id="radioName1" hidden/>
        <label for="radioName1" class="radio-beauty"></label>
    </label>
    <label>
        <span class="radio-name">后端工程师</span>
        <input type="radio" name="radioName" id="radioName2" hidden/>
        <label for="radioName2" class="radio-beauty"></label>
    </label>
    <label>
        <span class="radio-name">全栈工程师</span>
        <input type="radio" name="radioName" id="radioName3" hidden/>
        <label for="radioName3" class="radio-beauty"></label>
    </label>
</div>

SCSS:

.radio-beauty-container {
    font-size: 0;
    $bgc: green;
    %common {
        padding: 2px;
        background-color: $bgc;
        background-clip: content-box;
    }
    .radio-name {
        vertical-align: middle;
        font-size: 16px;
    }
    .radio-beauty {
        width: 18px;
        height: 18px;
        box-sizing: border-box;
        display: inline-block;
        border: 1px solid $bgc;
        vertical-align: middle;
        margin: 0 15px 0 3px;
        border-radius: 50%;
        &:hover {
            box-shadow: 0 0 7px $bgc;
            @extend %common;
        }
    }
    input[type="radio"]:checked+.radio-beauty {
        @extend %common;
    }
}

美化radio单选框在线预览地址:点击我呀
美化checkbox复选框在线预览地址:点击我呀

更多关于这方面的介绍和例子可以参看张鑫旭大神的这篇文章:CSS radio/checkbox单复选框元素显隐技术

2.CSS一些选择器

HTML:

<div class="wrapper">
  <p class="test1">1</p>
  <p class="test2">2</p>
  <p class="test3">3</p>
  <p class="test4">4</p>
  <p class="test5">5</p>
</div>

CSS:

p{
  width:20px;
  line-height:20px;
  border:1px solid gray;
  text-align:center;
  font-weight: 700;
}
E + F: 相邻兄弟选择器 选择匹配F的元素,且该元素为所匹配E元素后面相邻的位置。
.test1+p{
  background-color:green;
}

E > F:子包含选择器 选择匹配F的元素,且该元素为所匹配E元素的子元素。
.wrapper>p{
  background-color:green;
}

E ~ F: 选择后面的兄弟节点们
.test2~p{
  background-color:green;
}

E::after,E::before: 伪元素选择器 在匹配E的元素后面(前面)插入内容
.test2::before{
  background-color:green;
  content:"前"
}
.test2::after{
  background-color:green;
  content:"后"
}

:not(E) 选择非 元素的每个元素。
.wrapper>:not(.test2){
  background-color:green;
}

:checked input:checked 选择每个被选中的input元素。

HTML:

<input type="radio" name="" id="" />

<span>3333</span>

CSS:

input:checked+span{
  border:10px solid red;
}





这里只提一下本文要用到的CSS选择器,更多关于CSS3强大的选择器请移步这里:全面整理 CSS3 选择器的用法




实现评分模块

HTML:

 <div class="rating">
        <input type="radio" id="star5" name="rating" value="5" hidden/>
        <label for="star5"></label>
        <input type="radio" id="star4" name="rating" value="4" hidden/>
        <label for="star4"></label>
        <input type="radio" id="star3" name="rating" value="3" hidden/>
        <label for="star3"></label>
        <input type="radio" id="star2" name="rating" value="2" hidden/>
        <label for="star2"></label>
        <input type="radio" id="star1" name="rating" value="1" hidden/>
        <label for="star1"></label>
    </div>

关于input标签的隐藏,我这里只要用hidden属性实现隐藏,当然还有很多实现方式,只要input不占据文档的位置但是看不见就OK,我们需要隐藏单选框,且为可用性隐藏。这里还有几种方式仅供大家参考:

1. display: none;

 .rating >input {
        display: none;
    }

2. css3的clip

 .rating >input {
        position: absolute;
        clip: rect(0 0 0 0);
    }

3.opcity

.rating >input {
        position: absolute;
        opacity: 0;
    }

CSS:

    .rating {
        font-size: 0;
        display: table;
    }

    .rating > label {
        color: #ddd;
        float: right;
    }

    .rating > label:before {
        padding: 5px;
        font-size: 24px;
        line-height: 1em;
        display: inline-block;
        content: "★";
    }

    .rating > input:checked ~ label,
    .rating:not(:checked) > label:hover,
    .rating:not(:checked) > label:hover ~ label {
        color: #FFD700;
    }

    .rating > input:checked ~ label:hover,
    .rating > label:hover ~ input:checked ~ label,
    .rating > input:checked ~ label:hover ~ label {
        opacity: 0.5;
    }

在线预览地址





展示评分模块

用户评完分之后,会看到展示的分数,假设五个星星,满分10分。

展示评分就比较简单,放两个一模一样的html,五角星颜色不同,灰色的放在下面,评分的亮色放在上面,然后按照百分比显示分数。

HTML:

 <div class="star-rating">
        <div class="star-rating-top" style="width:50%">
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
        </div>
        <div class="star-rating-bottom">
            <span></span>
            <span></span>
            <span></span>
            <span></span>
            <span></span>
        </div>
    </div>

CSS:

.star-rating {
        unicode-bidi: bidi-override;
        color: #ddd;
        font-size: 0;
        height: 25px;
        margin: 0 auto;
        position: relative;
        display: table;
        padding: 0;
        text-shadow: 0px 1px 0 #a2a2a2;
    }

    .star-rating span {
        padding: 5px;
        font-size: 20px;
    }
    
    .star-rating span:after {
        content: "★";
    }

    .star-rating-top {
        color: #FFD700;
        padding: 0;
        position: absolute;
        z-index: 1;
        display: block;
        top: 0;
        left: 0;
        overflow: hidden;
        white-space: nowrap;
    }

    .star-rating-bottom {
        padding: 0;
        display: block;
        z-index: 0;
    }

当接口返回的分数是5分的时候,刚好占据一半的星星,2星半,只要计算出百分比就行,只用管数据,用上vue.js数据驱动的特点来动态展示样式这个简直不要太容易。

在线预览地址:

本文方法好处在于,纯CSS驱动,各种切换根本不需要JS,省了不少JS,对于类似这种需求大家也可以举一反三,这里只提供一些思路,没有细说;同时图片背景比较小或者可以直接不使用图片,比起使用图片节省不少资源,和提高些许性能。但是,学习以及理解成本稍高,可能并不适用于所有同行,因此,此文适合喜欢“折腾”的童鞋。

@phpcxy
Copy link

phpcxy commented Mar 31, 2017

一个页面同时有多个评分的话,好像只有一个能生效

@jawil
Copy link
Owner Author

jawil commented Mar 31, 2017

两个的话name是一样的话,说明这个只是一组,要把name改变一下,ID也改一下就ok @phpcxy

@phpcxy
Copy link

phpcxy commented Mar 31, 2017

抱歉~多个评分也是可以的。之前是我多个评分的input id 和label for 都是相同的。

@zuibunan
Copy link

跨平台会有问题,不同字体渲染出的大小不一样

@jawil
Copy link
Owner Author

jawil commented Mar 31, 2017

恩,确实存在这个问题,最好样式统一一下字体,因为不同字体好像行高有所区别,我也忘了,采用第二种矢量字体图标更好一点。@zuibunan

@Elvira-Ho
Copy link

这个只实现了 点击星星的效果,如何实现点击星星 显示不同分数?

@pushuo
Copy link

pushuo commented Dec 15, 2017

关于评星,这个方式存在一个 BUG,就是宫选择的 label 是顺序的,而input 是倒序,意思就说 当选择五星的时候 实际提交的是1星。 我的解决是 页面上循环倒序,不知道这样可好?

@WangNianyi2001
Copy link

background-clip: text 行吗 *盯~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants