1.最终效果
Rate 评分组件是一个用于展示和收集用户评分的交互组件,常用于商品评价、服务评分等场景。本文将从实现思路、核心功能方面详细介绍如何实现一个功能完善的评分组件。
最终实现效果:
2.核心思路
核心思路:动态计算每颗星的填充比例
getStarWidth(index) {
if (this.disabled && this.showScore) {
const decimal = this.value - index;
return decimal >= 1 ? '100%' : `${Math.max(decimal, 0) * 100}%`;
}
if (this.hoverValue === -1) {
const decimal = this.value - index;
return decimal >= 1 ? '100%' : '0%';
}
const decimal = this.hoverValue - index;
return decimal >= 1 ? '100%' : '0%';
},
3.部分代码
<template>
<div class="my-rate" :class="{ 'is-disabled': disabled && !showScore }">
<div
v-for="(item, index) in max"
:key="index"
class="my-rate__item"
:class="{ 'is-disabled': disabled && !showScore }"
@mousemove="handleMouseMove($event, item)"
@mouseleave="handleMouseLeave"
@click="handleClick(item)"
>
<!-- 底层灰色星 -->
<slot :size="size" :color="voidColor" :index="index" :item="item">
<svg
t="1747122044589"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="4627"
:width="size"
:height="size"
:fill="voidColor"
>
<path
d="M781.186088 616.031873q17.338645 80.573705 30.59761 145.848606 6.119522 27.537849 11.219124 55.075697t9.689243 49.976096 7.649402 38.247012 4.079681 19.888446q3.059761 20.398406-9.179283 27.027888t-27.537849 6.629482q-5.099602 0-14.788845-3.569721t-14.788845-5.609562l-266.199203-155.027888q-72.414343 42.836653-131.569721 76.494024-25.498008 14.278884-50.486056 28.557769t-45.386454 26.517928-35.187251 20.398406-19.888446 10.199203q-10.199203 5.099602-20.908367 3.569721t-19.378486-7.649402-12.749004-14.788845-2.039841-17.848606q1.01992-4.079681 5.099602-19.888446t9.179283-37.737052 11.729084-48.446215 13.768924-54.055777q15.298805-63.23506 34.677291-142.788845-60.175299-52.015936-108.111554-92.812749-20.398406-17.338645-40.286853-34.167331t-35.697211-30.59761-26.007968-22.438247-11.219124-9.689243q-12.239044-11.219124-20.908367-24.988048t-6.629482-28.047809 11.219124-22.438247 20.398406-10.199203l315.155378-28.557769 117.290837-273.338645q6.119522-16.318725 17.338645-28.047809t30.59761-11.729084q10.199203 0 17.848606 4.589641t12.749004 10.709163 8.669323 12.239044 5.609562 10.199203l114.231076 273.338645 315.155378 29.577689q20.398406 5.099602 28.557769 12.239044t8.159363 22.438247q0 14.278884-8.669323 24.988048t-21.928287 26.007968z"
p-id="4628"
></path>
</svg>
</slot>
<div class="star-filled" :style="{ width: getStarWidth(index) }">
<!-- 上层彩色星(通过遮罩控制显示比例) -->
<slot :size="size" :color="getIconColor(item)" :index="index" :item="item">
<svg
t="1747122044589"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="4627"
:width="size"
:height="size"
:fill="getIconColor(item)"
>
<path
d="M781.186088 616.031873q17.338645 80.573705 30.59761 145.848606 6.119522 27.537849 11.219124 55.075697t9.689243 49.976096 7.649402 38.247012 4.079681 19.888446q3.059761 20.398406-9.179283 27.027888t-27.537849 6.629482q-5.099602 0-14.788845-3.569721t-14.788845-5.609562l-266.199203-155.027888q-72.414343 42.836653-131.569721 76.494024-25.498008 14.278884-50.486056 28.557769t-45.386454 26.517928-35.187251 20.398406-19.888446 10.199203q-10.199203 5.099602-20.908367 3.569721t-19.378486-7.649402-12.749004-14.788845-2.039841-17.848606q1.01992-4.079681 5.099602-19.888446t9.179283-37.737052 11.729084-48.446215 13.768924-54.055777q15.298805-63.23506 34.677291-142.788845-60.175299-52.015936-108.111554-92.812749-20.398406-17.338645-40.286853-34.167331t-35.697211-30.59761-26.007968-22.438247-11.219124-9.689243q-12.239044-11.219124-20.908367-24.988048t-6.629482-28.047809 11.219124-22.438247 20.398406-10.199203l315.155378-28.557769 117.290837-273.338645q6.119522-16.318725 17.338645-28.047809t30.59761-11.729084q10.199203 0 17.848606 4.589641t12.749004 10.709163 8.669323 12.239044 5.609562 10.199203l114.231076 273.338645 315.155378 29.577689q20.398406 5.099602 28.557769 12.239044t8.159363 22.438247q0 14.278884-8.669323 24.988048t-21.928287 26.007968z"
p-id="4628"
></path>
</svg>
</slot>
</div>
</div>
<span v-if="showText" class="my-rate__text">{{ currentText }}</span>
<span v-if="showScore" class="my-rate__score">{{ displayValue }}</span>
</div>
</template>