星级展示应该是个挺常见的需求,刚好项目中有碰到。所以分享一下实现的过程,方便以后复用
一、需求分析
先看个常见的星级展示 UI
分析组件需要的参数
属性 | 含义 | 默认值 | 类型 |
---|---|---|---|
value | 当前星级 | 4.5 | Number |
maxValue | 总星级 | 5 | Number |
activeColor | 星级颜色 | #ff7e28 | String |
先看「当前星级」这个参数,考虑到有小数。所以不能用半星、全星之类的简单方法。
图示里的 4.3
表示前 4 个星星都完全展示,最后一个展示宽度的 0.3
。所以我们构建一个父子关系,让高亮部分的宽度,按父元素宽度的百分比展示即可
为了控制「星级颜色」,建议用 svg 或者 font-icon(用图片也可以,多加两个参数传 activeImage
、placeholderImage
就行)
二、准备图标
先找个星星图标。我是在 iconfont 上找的。过程不赘述了,直接帖代码
/* icon-star.css */
@font-face {
font-family: "iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALcAAsAAAAABogAAAKNAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGVgCCcAqBCIERATYCJAMICwYABCAFhGcHMhvFBci+QDaGA61+ohmjWdUpRTi3djdG3vv+AydFVwwi+NoP3Xd3AQDFIBMXDWjjomJJRrYKAHVlJ7a6QkUXbf2//7U2h1mHNAnaykXU02lemf9XdHawZqFapfEojUazRCPehY40QHws1yy4q+8HVWZx/WBKLK7PSs3l5Ym0eW581PWGkt85n+dyfAvogNYHsbtrbRprc9Q7oPJAOtDmtrEIe5En3jB2wQu8RKDRklUDm7vHLZg5dSSDOUPs3iBrjNNQOzDGo4oCC/VCbcPKIv5Dup0C7kSfj3/qMUZSk9ktOyc7Pry84IzJl9Wl5RuXy4WBrZExBxTioDG7ZVAwdlBjdsgIOFZj8FNVBR5xVKdgf51d3wyGYS74yZMftZDgsQaaAtYmtRBTte963PSzq+7WbZR++rG3cy+5z8/7ug/25y+g4Qk+6Mun4R/12ePXZ5/6/npxvuf7/kj6LfYsJntdN/3Irfrfx5TpT9GwDlCVHzMCwVs+PGb9sckCbrpfR+GqAsP81O/cG/wks4EdxVBsdyqa1NidTJ3c0OgkqNjBTt9TbV13VAz1uib46wxlyOqNEIWZQY0mK6hVbwONZu2sb9KlMChKAybcAIR2z0hafUPW7pUozBdq9OqjVrt/NDqKlt2aTIS3xeQI+eheYOjlmXKtEmltkD5JHK5LdYirECbQNqxyYY8y4jnmVKe6LqJQcZ7iLj2PkiTHgvOIPDECkWLRNFXTWwwvT2ELIw5BfMh1AYU8uYwK1KX4fQ1EO5Fw8ECik2WVEGpHNoM1gNgzZYPaHmSfyimtTggFKQzyKbTLh5FEIoeK5mER4hGGYEK6sMh01NRQk7G9Jf21fdDItqdwKrP6yzDTnhMAAAAA') format('woff2')
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-star:before {
content: "\e627";
}
新建一个 icon-star.css
文件,把代码帖进去。vue 文件引入之后,在任意标签上加两个属性,就可以展示出我们需要的「星星」了
<template>
<div class="iconfont icon-star">星星</div>
</template>
<script>
import "./icon-star.css";
export default {}
</script>
三、代码编写
先实现一个可以展示指定宽度的星星
<template>
<div>
<div class="iconfont icon-star star-item star-placeholder">
<div
class="iconfont icon-star star-item star-active"
:style="{ width: '60%' }"
/>
</div>
星星
</div>
</template>
<script>
export default {};
</script>
<style scoped>
@font-face {
font-family: "iconfont";
src: url("data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALcAAsAAAAABogAAAKNAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGVgCCcAqBCIERATYCJAMICwYABCAFhGcHMhvFBci+QDaGA61+ohmjWdUpRTi3djdG3vv+AydFVwwi+NoP3Xd3AQDFIBMXDWjjomJJRrYKAHVlJ7a6QkUXbf2//7U2h1mHNAnaykXU02lemf9XdHawZqFapfEojUazRCPehY40QHws1yy4q+8HVWZx/WBKLK7PSs3l5Ym0eW581PWGkt85n+dyfAvogNYHsbtrbRprc9Q7oPJAOtDmtrEIe5En3jB2wQu8RKDRklUDm7vHLZg5dSSDOUPs3iBrjNNQOzDGo4oCC/VCbcPKIv5Dup0C7kSfj3/qMUZSk9ktOyc7Pry84IzJl9Wl5RuXy4WBrZExBxTioDG7ZVAwdlBjdsgIOFZj8FNVBR5xVKdgf51d3wyGYS74yZMftZDgsQaaAtYmtRBTte963PSzq+7WbZR++rG3cy+5z8/7ug/25y+g4Qk+6Mun4R/12ePXZ5/6/npxvuf7/kj6LfYsJntdN/3Irfrfx5TpT9GwDlCVHzMCwVs+PGb9sckCbrpfR+GqAsP81O/cG/wks4EdxVBsdyqa1NidTJ3c0OgkqNjBTt9TbV13VAz1uib46wxlyOqNEIWZQY0mK6hVbwONZu2sb9KlMChKAybcAIR2z0hafUPW7pUozBdq9OqjVrt/NDqKlt2aTIS3xeQI+eheYOjlmXKtEmltkD5JHK5LdYirECbQNqxyYY8y4jnmVKe6LqJQcZ7iLj2PkiTHgvOIPDECkWLRNFXTWwwvT2ELIw5BfMh1AYU8uYwK1KX4fQ1EO5Fw8ECik2WVEGpHNoM1gNgzZYPaHmSfyimtTggFKQzyKbTLh5FEIoeK5mER4hGGYEK6sMh01NRQk7G9Jf21fdDItqdwKrP6yzDTnhMAAAAA")
format("woff2");
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-star:before {
content: "\e627";
}
.star-item {
display: inline-block;
font-size: 40px;
}
.star-placeholder {
position: relative;
}
.star-active {
position: absolute;
top: 0;
left: 0;
color: red;
overflow: hidden;
}
</style>
再加个循环,并计算每个 star-item
该展示的宽度。最终代码如下:
<template>
<div>
<div
v-for="index in maxValue"
:key="index"
class="iconfont icon-star star-item star-placeholder"
>
<div
class="iconfont icon-star star-item star-active"
:style="{ width: getWidth(index), color: activeColor }"
/>
</div>
<span class="star-value">
{{ starValue }}
</span>
</div>
</template>
<script>
import "./icon-star.css";
export default {
props: {
maxValue: {
type: Number,
default: 5,
},
value: {
type: Number,
default: 4.5,
},
activeColor: {
type: String,
default: "#ff7e28",
},
},
computed: {
starValue() {
return this.value.toFixed(1);
},
},
methods: {
getWidth(index) {
const gap = this.value - index + 1;
if (gap >= 1) {
return "100%";
} else if (gap <= 0) {
return 0;
} else {
return gap * 100 + "%";
}
},
},
};
</script>
<style scoped>
.star-item {
display: inline-block;
font-size: 40px;
}
.star-placeholder {
position: relative;
color: #e9e9e9;
}
.star-active {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}
.star-value {
font-size: 40px;
margin-left: 10px;
}
</style>
四、效果展示
<star-level :value="4.1" />
<star-level :value="3" activeColor="blue" />
<star-level :value="8.8" :maxValue="10" activeColor="green" />
五、选择星级
既然展示都做出来了,不如再稍微改改,做个选择星级的组件
思路就是判断鼠标在当前星级的位置,并结合这是第几颗星,计算出 value 值
<template>
<div>
<div
v-for="index in maxValue"
:key="index"
@mousemove="mousemoveStarItem($event, index)"
class="iconfont icon-star star-item star-placeholder"
>
<div
class="iconfont icon-star star-item star-active"
:style="{ width: getWidth(index), color: activeColor }"
/>
</div>
<span class="star-value" :style="{ color: activeColor }">
{{ starValue }}
</span>
</div>
</template>
<script>
import "./icon-star.css";
export default {
data() {
return {
value: 0,
};
},
props: {
maxValue: {
type: Number,
default: 5,
},
activeColor: {
type: String,
default: "#ff7e28",
},
},
computed: {
starValue() {
return this.value.toFixed(1);
},
},
methods: {
getWidth(index) {
const gap = this.value - index + 1;
if (gap >= 1) {
return "100%";
} else if (gap <= 0) {
return 0;
} else {
return gap * 100 + "%";
}
},
mousemoveStarItem(e, index) {
const width = e.currentTarget.offsetWidth;
this.value = index + e.offsetX / width - 1;
},
},
};
</script>
<style scoped>
.star-item {
display: inline-block;
font-size: 40px;
}
.star-placeholder {
cursor: pointer;
position: relative;
color: #e9e9e9;
}
.star-active {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
}
.star-value {
font-size: 40px;
margin-left: 10px;
}
</style>
附录:
CodeSandBox 代码示例:https://codesandbox.io/s/star...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。